Proving concatenation of language is associative in Agda - regular-language

I am new to the language Agda, and I am working on formal languages using Agda.
I've got some problems when proving the concatenation of languages is associative. The proof will be yellow highlighted as Agda could not find the words for "++Assoc" in the following code:
LangAssoc : ∀{Σ}{l1 l2 l3 : Language Σ}{w : Word Σ} → (LangConc l1 (LangConc l2 l3)) w → (LangConc (LangConc l1 l2) l3) w
LangAssoc {l1 = l1} (conc x (conc y z)) = conc (conc (subst l1 (++Assoc _ _ _) x) y) z
Where the "++Assoc" is the proof of the associativity of the concatenation of the lists.
The language and concatenation is defined as following:
Language : ℕ → Set₁
Language a = Word a → Set
data LangConc {Σ} (l1 l2 : Language Σ) : Language Σ where
conc : ∀{w1 w2} → l1 w1 → l2 w2 → (LangConc l1 l2) (w1 ++ w2)
So I would like to ask if anyone can explain on what am I doing wrong in this situation and give me some hint on how to solve the problem.
Edit 1: I've tried another way by using subst outside the concatenation, but I got stuck in this situation:
LangAssoc : ∀{Σ} {l1 l2 l3 : Language Σ} {w : Word Σ} → (LangConc l1 (LangConc l2 l3)) w → (LangConc (LangConc l1 l2) l3) w
LangAssoc {Σ} {l1 = l1} {l2 = l2} {l3 = l3} (conc x (conc y z)) = subst {!!} (++Assoc {Σ} _ _ _) (conc {Σ} (conc {Σ} x y) z)
Edit 2: I've just tried with the following code and results in a error message.
LangAssoc : ∀{Σ} {l1 l2 l3 : Language Σ} {w : Word Σ} → (LangConc l1 (LangConc l2 l3)) w → (LangConc (LangConc l1 l2) l3) w
LangAssoc {l1 = l1} {w = w} (conc x (conc y z)) = ?
w1 ++ w2 != w of type List (Fin Σ)
when checking that the pattern conc x (conc y z) has type
LangConc l1 (LangConc l2 l3) w
Edit 3: Just had another try by breaking the word w into three pieces as suggested.
LangAssoc : ∀{Σ : ℕ}{l1 l2 l3 : Language Σ}{w1 w2 w3 : Word Σ}
→ (LangConc l1 (LangConc l2 l3)) (w1 ++ w2 ++ w3)
→ (LangConc (LangConc l1 l2) l3) ((w1 ++ w2) ++ w3)
LangAssoc (conc {w1} x (conc {w2} {w3} y z))
= subst (LangConc (LangConc _ _) _) (++Assoc w1 w2 w3) (conc (conc x y) z)
Error message:
w4 != w1 of type List (Fin Σ)
when checking that the pattern conc {w1} x (conc {w2} {w3} y z) has
type LangConc l1 (LangConc l2 l3) (w1 ++ w2 ++ w3)
where List (Fin Σ) is the definition of Word Σ.

The problem is that ++ is not injective (and even if it were, Agda wouldn't know about it) so when trying to solve x ++ (y ++ z) ?= _1 ++ (_2 ++ _3), there is no most general solution Agda could pick.
For instance saying _1 = [], _2 = x, _3 = y ++ z would work.
Edit: It does not really makes sense to introduce w after having pattern matched on LangConc l1 (LangConc l2 l3) w: w has been broken up in 3 parts (the ones correponding to l1, l2 and l3). It is much more interesting to introduce these parts:
LangAssoc (conc {w1} x (conc {w2} {w3} y z)) =
subst (LangConc (LangConc _ _) _) (++Assoc w1 w2 w3) (conc (conc x y) z)

Related

Lambda Calculus change of variable and application question

I am studying Haskell and I am learning what is an abstraction, substitution (beta equivalence), application, free and bound variables (alpha equivalence), but I have some doubts resolving these exercises, I don't know if my solutions are correct.
Make the following substitutions
1. (λ x → y x x) [x:= f z]
Sol. (\x -> y x x) =>α (\w -> y w w) =>α (\w -> x w w) =>β (\w -> f z w w)
2. ((λ x → y x x) x) [y:= x]
Sol. ((\x -> y x x)x) =>α (\w -> y w w)[y:= x] = (\w -> x w w)
3. ((λ x → y x) (λ y → y x) y) [x:= f y]
Sol. aproximation, i don't know how to do it: ((\x -> y x)(\y -> y x) y) =>β
(\x -> y x)y x)[x:= f y] =>β y x [x:= f y] = y f y
4. ((λ x → λ y → y x x) y) [y:= f z]
Sol aproximation, ((\x -> (\y -> (y x x))) y) =>β ((\y -> (y x x)) y) =>α ((\y -> (y x x)) f z)
Another doubt that I have is if can I run these expressions on this website? It is a Lambda Calculus Calculator but I do not know how to run these tests.
1. (λ x → y x x) [x:= f z]
Sol. (\x -> y x x) =>α (\w -> y w w) =>α (\w -> x w w) =>β (\w -> f z w w)
No, you can't rename y, it's free in (λ x → y x x). Only bound variables can be (consistently) α-renamed. But only free variables can be substituted, and there's no free x in that lambda term.
2. ((λ x → y x x) x) [y:= x]
Sol. ((\x -> y x x)x) =>α (\w -> y w w)[y:= x] = (\w -> x w w)
Yes, substituting x for y would allow it to be captured by the λ x, so you indeed must α-rename the x in (λ x → y x x) first to some new unique name as you did, but you've dropped the application to the free x for some reason. You can't just omit parts of a term, so it's ((\w -> y w w) x)[y:= x]. Now perform the substitution. Note you're not asked to perform the β-reduction of the resulting term, just the substitution.
I'll leave the other two out. Just follow the rules carefully. It's easy if you rename all bound names to unique names first, even if the renaming is not strictly required, for instance
((λ x → y x) (λ y → y x) y) [x:= f y] -->
((λ w → y w) (λ z → z x) y) [x:= f y]
The "unique" part includes also the free variables used in the substitution terms, that might get captured after being substituted otherwise (i.e. without the renaming being performed first, in the terms in which they are being substituted). That's why we had to rename the bound y in the above term, -- because y appears free in the substitution term. We didn't have to rename the bound x but it made it easier that way.

How to remove a universal quantifier in Lean?

I am working with two binary relations: g_o and pw_o, and I've defined pw_o below:
constants {A : Type} (g_o : A → A → Prop)
def pw_o (x y : A) : Prop := ∀ w : A, (g_o w x → g_o w y) ∧ (g_o y w → g_o x w)
I need to prove the following:
theorem prelim: ∀ x y z : A, g_o x y ∧ pw_o y z → g_o x z :=
I start with these tactics:
begin
intros,
cases a with h1 h2,
end
And I have this:
x y z : A,
h1 : g_o x y,
h2 : pw_o y z
⊢ g_o x z
Since pw_o is defined with a universal quantifier, I'd like to substitute w with x, then I would have (g_o x y → g_o x z) ∧ (g_o z x → g_o y x). After isolating the first conjunct with the "cases" tactic, I can use modus ponens on that first conjunct and h1.
How can I tell Lean to replace w in the definition of pw_o with x and replace x and y in the definition of pw_o with y and z, respectively?
Elimination of the universal quantifier is basically just application,
so to substitute w, with x just apply x to the instance h2 of pw_o y z.
theorem prelim': ∀ x y z : A, g_o x y ∧ pw_o y z → g_o x z :=
begin
intros,
cases a with h1 h2,
cases h2 x with h3 _,
sorry
end

Summing up all the nodes a tree with a generic type. (Haskell)

I have been trying to write a code which takes all the integers in a tree and return a sum of them. I'm trying to do this with type a, which is from a data time:
data Tree a = Nil | Value a (Tree a) (Tree a)
deriving Show
and we want to use:
tree = Value 2 (Value 2 (Value 2 Nil Nil) Nil) (Value 2 Nil Nil)
and my code is as follow:
countTree :: (a -> a -> a) -> a -> Tree a -> a
countTree p k (Nil) = h
countTree p k (Value x y z) = x (+) (countTree p k y) (+) (countTree p k z)
and I want to run my code as countTree (+) 0 tree and the results should return 8.
The problem is that when I run my code it tells me that x has four arguments but it's type a has zero which I honestly don't understand why. I've modifying sections of my code, but no success once so ever, I could really use some assistance.
x (+) (countTree p k y) (+) (countTree p k z)
is attempting to treat x as a function, and pass to it as arguments all of
(+) (countTree p k y) (+) (countTree p k z)
If you want to have "x + recur left + recur right", you'd want something like:
x + (countTree p k y) + (countTree p k z)
I'm pretty sure however you actually want to use p, not + hard coded. Using prefix notation, you'd have to rearrange it a bit to something like :
(p (p x (countTree p k y)) (countTree p k z))
Or, you could use backticks to inline the calls to p as #bipll suggested:
x `p` (countTree p k y) `p` (countTree p k z)
A side note, but I'm also pretty sure you want h to be k.

Primitive Recursive function

In A tutorial on universality and expressiveness of fold chapter 4.1, it states that this pattern of recursion
h y [] = f y
h y (x:xs) = g y x xs (h y xs)
is primitive recursion, but I don't understand why the pattern
h [] = v
h (x:xs) = g x (h xs)
is not primitive recursion according to the definition of primitive recursive.
The value of h y' is still based on h y in the h (x:xs) = g x (h xs) if we let y = xs and y' = x:xs.
The primitive recursion scheme is parametric on the choice of f,g
h y [] = f y
h y (x:xs) = g y x xs (h y xs)
That is, we are free to choose f,g as we want, and h will be defined through primitive recursion.
In particular, we can choose
f = \y -> v
g = \y x xs -> g' x z
where g' is any other function picked by us. We then get
h y [] = v
h y (x:xs) = g' x (h y xs)
Now, if we let
h' xs = h () xs
we fix the y argument to an immaterial value so to recover the function in the question. Pedantically, h' is not obtained directly as an instance of the general form, so h' is technically not defined through the primitive recursion scheme seen above (i.e., it is not an instance of that). Sometimes, instead of y we find there many variables y1 .. yn allowing us to pick n=0 and remove the y as we want in this case.

How to set strictness in list comprehension?

I'm bit stuck how to rewrite following strict-evaluated list comprehension to use seq instead of bang pattern:
zipWith' f l1 l2 = [ f e1 e2 | (!e1, !e2) <- zip l1 l2 ]
Any idea ?
I've tried
zipWith' f l1 l2 = [ e1 `seq` e2 `seq` f e1 e2 | (e1, e2) <- zip l1 l2 ]
but this unfortunately does not force an evaluation to WHNF.
You can mechanically translate bang patterns into calls to seq
following the GHC
manual:
This:
zipWith' f l1 l2 = [ f e1 e2 | (!e1, !e2) <- zip l1 l2 ]
Becomes Too lazy:
zipWith' f l1 l2 =
[ f e1 e2
| e <- zip l1 l2
, let t = case e of (x,y) -> x `seq` y `seq` (x,y)
, let e1 = fst t
, let e2 = snd t
]
Which is more concisely written as (too lazy):
zipWith' f l1 l2 =
[ f e1 e2
| e <- zip l1 l2
, let (e1,e2) = case e of (x,y) -> x `seq` y `seq` (x,y)
]
Though I'd write it as (wrong, too lazy)
zipWith' f l1 l2 = zipWith (\x y -> uncurry f (k x y)) l1 l2
where
k x y = x `seq` y `seq` (x, y)
You could also move the strictness hint to a data structure:
data P = P !Integer !Integer
zipWith' f l1 l2 = [ f e1 e2 | P e1 e2 <- zipWith P l1 l2 ]
Or as:
zipWith' f l1 l2 = [ f e1 e2 | (e1, e2) <- zipWith k l1 l2 ]
where
k x y = x `seq` y `seq` (x,y)
The essential thing you want the strict zipWith to do is to evaluate the elements of the list at the time the cons cell is forced. Your function does not do that. Just try
main = print $ length $ zipWith' undefined [1..10] [1..100]
It's a fluke of the strictness analysis when you use (+) that makes it work.
The function you want is something like this:
zipW f (x:xs) (y:ys) = let z = f x y in seq z (z : zipW f xs ys)
zipW _ _ _ = []
You cannot decouple the production of the cons cell from the production of the value, because the former should force the latter.
To explain the original poster's example and some of Don's noted behavior. Using (>>=) as a concise concatMap, the original example translates into:
zip l1 l2 >>= \(!e1, !e2) -> [f e1 e2]
The second example is:
zip l1 l2 >>= \(e1, e2) -> [e1 `seq` e2 `seq` f e1 e2]
But what the desugared first version actually further desugars into is:
zip l1 l2 >>= \(e1, e2) -> e1 `seq` e2 `seq` [f e1 e2]
e1 and e2 get forced when the concat in concatMap flattens these singleton lists which provides the "force as I go along" behavior.
While this isn't what one would normally think of as a "strict" zipWith, it is not a fluke that it works for the fibs example.

Resources