Syntax error when trying to use dependent types in GF - gf

I'm trying to play with dependent types, but I've gotten stuck on a syntax error that I can't seem to find the cause for. It's on my first lin definition in the concrete syntax.
For context, I've been reading Inari's embedded grammars tutorial, and I'm interested in making functions which I can call from the outside, but which won't actually be used in linearisation unless I do it manually from Haskell. Please let me know if you know a better way to achieve this.
My abstract syntax:
cat
S;
Pre_S Clude ;
VP Clude ;
NP Clude ;
V2 ;
N ;
Clude ;
fun
Include, Exclude : Clude ;
To_S : Pre_S Include -> S ;
Pred : (c : Clude) -> NP c -> VP c -> Pre_S c ;
Compl : (c : Clude) -> V2 -> NP c -> VP c ;
-- I'd like to call this one from Haskell
Scramble : (c : Clude) -> VP c -> VP Exclude ;
To_NP : (c : Clude) -> N -> NP c ;
Bob, Billy, John, Mary, Lisa : N ;
Liked, Loved, Hated : V2 ;
Concrete:
lincat
Pre_S, S, V2, NP, N = {s : Str} ;
VP = {fst : Str ; snd : Str} ;
lin
To_S pre = {s = pre.s} ;
Pred _ sub vp = {s = sub ++ vp.fst ++ vp.snd} ;
Compl _ ver obj = {fst = ver ; snd = obj} ;
Scramble _ vp = {fst = vp.snd ; snd = vp.fst } ;
To_NP _ n = {s = n.s} ;
Bob = {s = "Bob"} ; --and seven more like this
Is it because this isn't possible this way, or did I do something wrong that I'm just not managing to find?

1. Fixing the syntax errors
None of the errors in your code is due to dependent types. The first issue, the actual syntax error is this:
To_S pre = {s = pre.s} ;
pre is a reserved word in GF, you can't used it as a variable name. You can write e.g.
To_S pr = {s = pr.s} ;
or shorter—since To_S just looks like a coercion function, you can do this:
To_S pr = pr ;
After fixing that, you get new error messages:
Happened in linearization of Pred
type of sub
expected: Str
inferred: {s : Str}
Happened in linearization of Compl
type of ver
expected: Str
inferred: {s : Str}
Happened in linearization of Compl
type of obj
expected: Str
inferred: {s : Str}
These are fixed as follows. You can't put a {s : Str} into a field that is supposed to contain a Str, so you need to access the sub.s, ver.s and obj.s.
Pred _ sub vp = {s = sub.s ++ vp.fst ++ vp.snd} ;
Compl _ ver obj = {fst = ver.s ; snd = obj.s} ;
Full grammar after fixing the syntax errors
This works for me.
lin
To_S pr = pr ;
Pred _ sub vp = {s = sub.s ++ vp.fst ++ vp.snd} ;
Compl _ ver obj = {fst = ver.s ; snd = obj.s} ;
Scramble _ vp = {fst = vp.snd ; snd = vp.fst } ;
To_NP _ n = {s = n.s} ;
Bob = {s = "Bob"} ;
Loved = {s = "loved"} ; -- just add rest of the lexicon
Include, Exclude = {s = ""} ; -- these need to have a linearisation, otherwise nothing works!
2. Tips and tricks on working with your grammar
With the lexicon of Bob and Loved, it generates exactly one tree. When I use the command gt (generate_trees), if I don't give a category flag, it automatically uses the start category, which is S.
> gt | l -treebank
To_S (Pred Include (To_NP Include Bob) (Compl Include Loved (To_NP Include Bob)))
Bob loved Bob
Parsing in S works too:
p "Bob loved Bob"
To_S (Pred Include (To_NP Include Bob) (Compl Include Loved (To_NP Include Bob)))
Parsing VPs: add a linref
With the current grammar, we can't parse any VPs:
> p -cat="VP ?" "Bob loved"
The parser failed at token 2: "loved"
> p -cat="VP ?" "loved Bob"
The parser failed at token 2: "Bob"
That's because the lincat of VP is discontinuous and doesn't have a single s field. But if you would like to parse also VPs, you can add a linref for the category of VP, like this:
linref
VP = \vp -> vp.fst ++ vp.snd ;
Now it works to parse even VPs, sort of:
Cl> p -cat="VP ?" "loved Bob"
Compl ?3 Loved (To_NP ?3 Bob)
These weird question marks are metavariables, and we'll fix it next.
Metavariables
If you've read my blog, you may have already seen this bit about metavariables: https://inariksit.github.io/gf/2018/08/28/gf-gotchas.html#metavariables-or-those-question-marks-that-appear-when-parsing
GF has a weird requirement that every argument needs to contribute with a string (even an empty string!), otherwise it isn’t recognised when parsing. This happens even if there is no ambiguity.
We have already linearised Include and Exclude with empty strings—they must have some linearisation in any case, otherwise the whole grammar doesn't work. So we need to add the Clude's empty string into all of the linearisations, otherwise GF parser is confused.
In all those linearisations where you just marked the Clude argument with underscore, we do this now. Doesn't matter which field we add it to, it's just an empty string and makes no effect in the output.
Pred _clu sub vp = {s = sub.s ++ vp.fst ++ vp.snd ++ _clu.s} ;
Compl _clu ver obj = {fst = ver.s ; snd = obj.s ++ _clu.s} ;
Scramble _clu vp = {fst = vp.snd ; snd = vp.fst ++ _clu.s} ;
To_NP _clu n = {s = n.s ++ _clu.s} ;
After this, parsing in VP works without question marks:
Cl> p -cat="VP ?" "loved Bob"
Compl Exclude Loved (To_NP Exclude Bob)
Compl Include Loved (To_NP Include Bob)
Cl> p -cat="VP ?" "Bob loved"
Scramble Exclude (Compl Exclude Loved (To_NP Exclude Bob))
I'm still getting metavariables????
So we fixed it, but why do I still get question marks when I gt and get a tree that uses Scramble?
> gt -cat="VP Exclude"
Compl Exclude Loved (To_NP Exclude Bob)
Scramble ?3 (Compl ?3 Loved (To_NP ?3 Bob))
Scramble Exclude (Scramble ?4 (Compl ?4 Loved (To_NP ?4 Bob)))
> gt -cat="VP Include"
Compl Include Loved (To_NP Include Bob)
That's because the Scramble operation truly suppresses its arguments. It's not just a case of GF compiler being stupid and refusing to cooperate even if it's obvious which argument there is, in this case there is no way to retrieve which argument it was: Scramble makes everything into a VP Exclude.
Full grammar after adding linref and including Clude's empty string
Just for the sake of completeness, here's all the changes.
lin
To_S pr = pr ;
Pred _clu sub vp = {s = sub.s ++ vp.fst ++ vp.snd ++ _clu.s} ;
Compl _clu ver obj = {fst = ver.s ; snd = obj.s ++ _clu.s} ;
Scramble _clu vp = {fst = vp.snd ; snd = vp.fst ++ _clu.s} ;
To_NP _clu n = {s = n.s ++ _clu.s} ;
Bob = {s = "Bob"} ;
Loved = {s = "loved"} ;
Include, Exclude = {s = ""} ;
linref
VP = \vp -> vp.fst ++ vp.snd ;
3. Alternative way of making this grammar without dependent types
If you would like to use your grammar from Python or the Haskell bindings for the C runtime, you are out of luck: the C runtime doesn't support dependent types. So here's a version of your grammar where we mimic the behaviour using parameters in the concrete syntax and the nonExist token (see https://inariksit.github.io/gf/2018/08/28/gf-gotchas.html#raise-an-exception).
I have kept the documentation minimal (because this answer is already so long 😅), but if you have any questions about some parts of this solution, just ask!
Abstract syntax
cat
S ; VP ; NP ; V2 ; N ;
fun
Pred : NP -> VP -> S ;
Compl : V2 -> NP -> VP ;
Scramble : VP -> VP ;
To_NPIncl,
ToNPExcl : N -> NP ;
Bob, Billy, John, Mary, Lisa : N ;
Liked, Loved, Hated : V2 ;
Concrete syntax
param
Clude = Include | Exclude ;
lincat
-- To mimic your categories VP Clude and NP Clude,
-- we add a parameter in VP and NP
VP = {fst, snd : Str ; clude : Clude} ;
NP = {s : Str ; clude : Clude} ;
-- The rest are like in your grammar
S, V2, N = {s : Str} ;
lin
-- No need for Pre_S, we can match NP's and VP's Clude in Pred
-- Only make a sentence if both's Clude is Include
Pred np vp = case <np.clude, vp.clude> of {
<Include,Include> => {s = np.s ++ vp.fst ++ vp.snd} ;
_ => {s = Predef.nonExist}
} ;
-- As per your grammar, V2 has no inherent Clude, but NP does
Compl v2 np = {
fst = v2.s ;
snd = np.s ;
clude = np.clude ;
} ;
-- Scramble doesn't look at it's argument VP's clude,
-- just makes it into Exclude automatically.
Scramble vp = {
fst = vp.snd ;
snd = vp.fst ;
clude = Exclude ;
} ;
-- Your grammar has the function To_NP : (c : Clude) -> N -> NP c ;
-- We translate it into two functions.
To_NPIncl n = n ** {clude = Include} ;
To_NPExcl n = n ** {clude = Exclude} ;
-- Finally, lexicon.
Bob = {s = "Bob"} ;
Loved = {s = "loved"} ;
Now when we generate all trees in category S, there is one that uses Scramble, but it doesn't have a linearisation.
> gt | l -treebank
ClParam: Pred (To_NPIncl Bob) (Compl Loved (To_NPIncl Bob))
ClParamEng: Bob loved Bob
ClParam: Pred (To_NPIncl Bob) (Scramble (Compl Loved (To_NPIncl Bob)))
ClParamEng:
Maybe a bit less elegant than your version, where the tree wasn't even generated, but this is just to demonstrate the different approaches. If you're working on Haskell and won't need to use C runtime, feel free to continue with your approach!

Related

How to use interfaces with parameterized tuple?

I have Coord function that transforms an n-dimensional size to the type of coordinates bounded by given size: Coord [2,3] = (Fin 2, Fin 3).
import Data.Fin
import Data.List
Size : Type
Size = List Nat
Coord : Size -> Type
Coord [] = ()
Coord s#(_ :: _) = foldr1 (,) $ map Fin s
I'd like to use show and other functions like (==) with Coord s:
foo : Coord s -> String
foo x = show x
Error: While processing right hand side of foo. Can't find an implementation for Show (Coord s).
22 | foo : Coord s -> String
23 | foo x = show x
^^^^^^
Earlier I tried to implement Show (Coord s), but looks like it's impossible. Here is linked question about it.
You can make your own list like data type:
data Coords : List Nat -> Type where
Nil : Coords []
(::) : Fin x -> Coords xs -> Coords (x :: xs)
toList : Coords xs -> List Nat
toList [] = []
toList (x::xs) = finToNat x :: toList xs
example : Coords [2, 3]
example = [1, 2]
Show (Coords xs) where
show cs = show $ toList cs
You can also try using Data.Vect.Quantifiers.All or Data.List.Quantifiers.All:
import Data.Vect
import Data.Vect.Quantifiers
example : All Fin [1, 2, 3]
example = [0, 1, 2]
-- not sure why this is isn't included with Idris
export
All (Show . p) xs => Show (All p xs) where
show pxs = "[" ++ show' "" pxs ++ "]"
where
show' : String -> All (Show . p) xs' => All p xs' -> String
show' acc #{[]} [] = acc
show' acc #{[_]} [px] = acc ++ show px
show' acc #{_ :: _} (px :: pxs) = show' (acc ++ show px ++ ", ") pxs
string : String
string = show example

Equality between paths

Using the cubical-demo library, I thought the following would be trivial to prove:
{-# OPTIONS --cubical #-}
open import Cubical.PathPrelude
foo : ∀ {ℓ} {A : Set ℓ} {x y : A} (p : x ≡ y) → trans refl p ≡ p
foo p = ?
But alas, it doesn't hold definitionally: trying to use refl fails with
primComp (λ _ → ;A) (~ i ∨ i) (λ { i₁ (i = i0) → ;x ; i₁ (i = i1) → p i₁ }) (refl i)
!= p i
of type ;A
and I don't know where to start.
No, sadly we lose some definitional equalities when using Path, because we don't know how to keep the system confluent if we were to add those reductions.
The eliminator of the Id type instead has the usual reduction rules.
https://github.com/Saizan/cubical-demo/blob/master/src/Cubical/Id.agda
In the case of the lemma you want to prove about trans you can find a proof at
https://github.com/Saizan/cubical-demo/blob/master/src/Cubical/Lemmas.agda
By the way, cubical-demo grew up organically, and we are starting fresh with hopefully a cleaner setup (altough with different primitives) at
https://github.com/agda/cubical
cubical has a better Id module for example:
https://github.com/agda/cubical/blob/master/Cubical/Core/Id.agda
Based on Saizan's answer I looked up the proof in cubical-demo and ported it to the new cubical library. I can see how it works out (as in, I can see that the value of the given path is x on all three designated edges) but I don't see yet how I would come up with a similar proof for a similar situation:
{-# OPTIONS --cubical #-}
module _ where
open import Cubical.Core.Prelude
refl-compPath : ∀ {ℓ} {A : Set ℓ} {x y : A} (p : x ≡ y) → compPath refl p ≡ p
refl-compPath {x = x} p i j = hcomp {φ = ~ j ∨ j ∨ i}
(λ { k (j = i0) → x
; k (j = i1) → p k
; k (i = i1) → p (k ∧ j)
})
x

Changing a mutable field in OCaml

When I run the following code I get a syntax error, although as far as I can tell the syntax is correct. This attempts to implement a queue structure, where the function from_list converts a list to a queue with the corresponding values. I wrote str_of_int_q to print the contents of a queue. x and y are supposed to be two nodes, with x at the head and y at the tail.
;; open Assert
type 'a qnode = {v: 'a;
mutable next: 'a qnode option}
type 'a queue = {mutable head: 'a qnode option;
mutable tail: 'a qnode option}
let from_list (l: 'a list) : 'a queue =
let rec loop (l2: 'a list) (qu: 'a queue) =
begin match l2 with
| [] -> qu
| [x] -> let y = {v = x; next = None} in
qu.head <- Some y; qu.tail <- Some y;
qu
| h1::h2::t -> let y = qu.head in
let z = {v = h1; next = y} in
qu.head <- Some z;
qu
end
in loop l {head = None; tail = None}
let str_of_int_q (q: int queue) : string =
let rec loop (r: int qnode option) (s: string) : string =
begin match r with
| None -> s
| Some n -> loop n.next (s ^ (string_of_int n.v))
end
in loop q.head ""
let x = {v = 1; next = None}
let y = {v = 2; next = None}
x.next <- Some y;
let z = {head = Some x; tail = Some y}
;; print_endline (str_of_int_q z)
My error:
line 32, characters 7-9:
Error: Syntax error
Line 32 is the line x.next <- Some y; and characters 7-9 indicate the <-. But I'm storing into a mutable field an object of the appropriate type, so I don't see what's going wrong.
Top-level statements are separated by ;; in OCaml. However, ;; is optional before several keywords, such as let, open, type, etc. This is why you don't need ;; most of the time.
In your case, ;; is needed to disambiguate between let y = {v = 2; next = None} and x.next <- Some y. The latter is an expression and doesn't start with a special keyword, so OCaml doesn't know to insert an implicit ;; here.
See also http://ocaml.org/learn/tutorials/structure_of_ocaml_programs.html#The-disappearance-of.
As explained there, you can either do
let y = {v = 2; next = None}
;; x.next <- Some y
or
let y = {v = 2; next = None}
let () = x.next <- Some y
This latter solution works because by introducing a dummy binding we're starting our statement with let, which disambiguates again.
Note: I've also removed the trailing ; from your code. ; is actually an infix operator that combines two expressions (by throwing the result of the first one away and returning the result of the second one). This is not what you want here.

How to prove a relation at compile-time in Lean?

Say I have a type:
inductive is_sorted {α: Type} [decidable_linear_order α] : list α -> Prop
| is_sorted_zero : is_sorted []
| is_sorted_one : Π (x: α), is_sorted [x]
| is_sorted_many : Π {x y: α} {ys: list α}, x < y -> is_sorted (y::ys) -> is_sorted (x::y::ys)
And it's decidable:
instance decidable_sorted {α: Type} [decidable_linear_order α] : ∀ (l : list α), decidable (is_sorted l)
If I have a particular list:
def l1: list ℕ := [2,3,4,5,16,66]
Is it possible to prove that it's sorted at "compile time"; to produce an is_sorted l1 at the top level?
I've tried def l1_sorted: is_sorted l1 := if H: is_sorted l1 then H else sorry, but I don't know how to show the latter case is impossible. I've also tried the simp tactic but that didn't seem to help.
I can prove it with #reduce, but it doesn't possible to assign the output of that to a variable.
You should be able to use dec_trivial to prove l1_sorted. This will try to infer an instance of decidable (is_sorted l1), and if the instance evaluates to is_true p, it will reduce to p.

Can't make type classes work in Lean

I'm having trouble understanding how to trigger the use of type classes by Lean. Here is an attempt at a small example:
section the_section
structure toto [class] (A : Type) := (rel : A → A → Prop) (Hall : ∀ a, rel a a)
definition P A := exists (a : A), forall x, x = a
parameter A : Type
variable HA : P A
lemma T [instance] {B : Type} [HPB : P B] : toto B := toto.mk (λ x y, x = y) (λ x, rfl)
include HA
example : toto A := _
-- this gives the error: don't know how to infer placeholder toto A
end the_section
The point is I would like Lean to see that it can use HA to deduce toto A from lemma T. What am I missing?
Once again, I had to post the question to find the answer. Hope this helps other people.
P needs to be a class, so we actually need to change
definition P A := exists (a : A), forall x, x = a
to
definition P [class] A := exists (a : A), forall x, x = a