Typed Lambda Calculus - type-inference

I want to find the type of the lambda expression \x y -> x y y. I proceed as follows.
We go in the reverse order of operations and "unpack" the expression. Assume the whole expression has type A. Then let y have type x1 and \x y -> x y have type B. Then A = B -> x1
We already know the type of y, and let \x y -> x be of the type C. Then B = C -> x1.
The type of \x y -> x is obviously x1->x2->x1. This is given to us in the previous exercise and makes sense because this expression takes in two arguments and returns the first.
Putting it all together we have that:
A = B -> x1 = C -> x1 -> x1 = (x1 -> x2 -> x1) -> x1 -> x1
The correct answer is somehow:
(x1 -> x1 -> x2) -> x1 -> x2
What am I doing wrong?

Here I just write stuff's types under it and go from there:
foo = \x y -> x y y
foo x y = x y y
a b : c
a b b : c
a b : b -> c
a : b -> b -> c
foo : a -> b -> c
~ (b -> b -> c) -> b -> c
And another one:
bar = \x y -> x (y x)
bar x y = x (y x)
a b : c
a (b a) : c
---------
b a : d
b : a -> d
a : d -> c
bar : a -> b -> c
~ (d -> c) -> ( a -> d) -> c
~ (d -> c) -> ((d -> c) -> d) -> c
But,
baz x = x x
a a a : b
a : a -> b
baz : a -> b
~ (a -> b) -> b
~ ((a -> b) -> b) -> b
~ (((a -> b) -> b) -> b) -> b
........
is an "infinite" type, i.e. the process of type derivation never stops.

Related

Pair equality on components

I would like to define a function of the following type
pairEquality :
(a, b : (obj1, obj2))
-> (fst a) = (fst b)
-> (snd a) = (snd b)
-> a = b
but I am a bit at loss with the implementation.
I know I can write
pairEquality' :
a1 = b1
-> a2 = b2
-> (a1, a2) = (b1, b2)
but that doesn't seem to compile where I need to use it (that's another question: what's the big difference between the two functions?)
The first implementation is pretty straight-forward. Once you split the tuples to get pairEquality (a, b) (x, y) prf1 prf2 = ?t and inspect the the hole, you already see, that the compiler infers that prf1 : a = x, prf2: b = y, alas:
pairEquality :
(a, b : (obj1, obj2))
-> (fst a) = (fst b)
-> (snd a) = (snd b)
-> a = b
pairEquality (x, y) (x, y) Refl Refl = Refl
In pairEquality you de-construct the tuples and in pairEquality' you construct the tuples. Latter is usually a better approach, and I guess you can change something in your caller function so it can be used.
I actually find out I can define pairEquality in terms of pairEquality' as
pairEquality :
(a, b : (obj1, obj2))
-> (fst a) = (fst b)
-> (snd a) = (snd b)
-> a = b
pairEquality (a1, a2) (b1, b2) = pairEquality' {a1} {a2} {b1} {b2}

Maybe.map over a function that returns a pair

I have a function with Type signature
a -> b -> (a, b)
And I have
Maybe a
How can I map such a function such that I can get
(a->b->(a,b)) -> Maybe a -> (Maybe a, b)
You need to slightly change the function definition
g : (a -> b -> (a,b)) -> Maybe a -> b -> (Maybe a, b)
g f ma b =
case ma of
Nothing -> (Nothing, b)
Just a ->
let
r = f a b
in
(Just (first r), second r)

Proving isomorphism between Martin-Lof's equality and Path Induction in Coq

I am studying Coq and trying to prove the isomorphism between Martin-Lof's equality and the Path Induction's one.
I defined the two equalities as follows.
Module MartinLof.
Axiom eq : forall A, A -> A -> Prop.
Axiom refl : forall A, forall x : A, eq A x x.
Axiom el : forall (A : Type) (C : forall x y : A, eq A x y -> Prop),
(forall x : A, C x x (refl A x)) ->
forall a b : A, forall p : eq A a b, C a b p.
End MartinLof.
Module PathInduction.
Axiom eq : forall A, A -> A -> Prop.
Axiom refl : forall A, forall x : A, eq A x x.
Axiom el : forall (A : Type) (x : A) (P : forall a : A, eq A x a -> Prop),
P x (refl A x) -> forall y : A, forall p : eq A x y, P y p.
End PathInduction.
And I defined the two functions involved in the isomorphism as follows.
Definition f {A} : forall x y: A, forall m: MartinLof.eq A x y, PathInduction.eq A x y.
Proof.
apply: (MartinLof.el A (fun a b p => PathInduction.eq A a b) _ x y m).
move=> x0.
exact: PathInduction.refl A x0.
Defined.
Definition g {A} : forall x y: A, forall p: PathInduction.eq A x y, MartinLof.eq A x y.
Proof.
apply: (PathInduction.el A x (fun a p => MartinLof.eq A x a) _ y p).
exact: MartinLof.refl A x.
Defined.
Now I am trying to define the following proof-terms, but I cannot move from the initial statement because I actually don't know what tactic to apply.
Definition pf1 {A}: forall x y: A, forall m: MartinLof.eq A x y,
eq m (g x y (f x y m)).
Definition pf2 {A} : forall x y: A, forall p: PathInduction.eq A x y,
eq p (f x y (g x y p)).
I through I could simplify the expression
(g x y (f x y m))
but I don't know how to do that. Does anyone know how I can go on on this proof?
Thanks in advance.
The problem is that your definition of the identity types is incomplete, because it does not specify how el interacts with refl. Here is a complete solution.
From mathcomp Require Import ssreflect.
Module MartinLof.
Axiom eq : forall A, A -> A -> Prop.
Axiom refl : forall A, forall x : A, eq A x x.
Axiom el : forall (A : Type) (C : forall x y : A, eq A x y -> Prop),
(forall x : A, C x x (refl A x)) ->
forall a b : A, forall p : eq A a b, C a b p.
Axiom el_refl : forall (A : Type) (C : forall x y : A, eq A x y -> Prop)
(CR : forall x : A, C x x (refl A x)),
forall x : A, el A C CR x x (refl A x) = CR x.
End MartinLof.
Module PathInduction.
Axiom eq : forall A, A -> A -> Prop.
Axiom refl : forall A, forall x : A, eq A x x.
Axiom el : forall (A : Type) (x : A) (P : forall a : A, eq A x a -> Prop),
P x (refl A x) -> forall y : A, forall p : eq A x y, P y p.
Axiom el_refl : forall (A : Type) (x : A) (P : forall y : A, eq A x y -> Prop)
(PR : P x (refl A x)),
el A x P PR x (refl A x) = PR.
End PathInduction.
Definition f {A} (x y: A) (m: MartinLof.eq A x y) : PathInduction.eq A x y.
Proof.
apply: (MartinLof.el A (fun a b p => PathInduction.eq A a b) _ x y m).
move=> x0.
exact: PathInduction.refl A x0.
Defined.
Definition g {A} (x y: A) (p: PathInduction.eq A x y) : MartinLof.eq A x y.
Proof.
apply: (PathInduction.el A x (fun a p => MartinLof.eq A x a) _ y p).
exact: MartinLof.refl A x.
Defined.
Definition pf1 {A} (x y: A) (m: MartinLof.eq A x y) : eq m (g x y (f x y m)).
Proof.
apply: (MartinLof.el A (fun x y p => p = g x y (f x y p))) => x0.
by rewrite /f MartinLof.el_refl /g PathInduction.el_refl.
Qed.
Definition pf2 {A} (x y: A) (m: PathInduction.eq A x y) : eq m (f x y (g x y m)).
Proof.
apply: (PathInduction.el A x (fun y p => p = f x y (g x y p))).
by rewrite /f /g PathInduction.el_refl MartinLof.el_refl.
Qed.
Alternatively, you could have just defined the two identity types as Coq inductive types, which would have given you the same principles without the need to state separate axioms; Coq's computation behavior already yields the equations you need. I have used pattern-matching syntax, but similar definitions are possible using the standard combinators eq1_rect and eq2_rect.
Inductive eq1 (A : Type) (x : A) : A -> Type :=
| eq1_refl : eq1 A x x.
Inductive eq2 (A : Type) : A -> A -> Type :=
| eq2_refl x : eq2 A x x.
Definition f {A} {x y : A} (p : eq1 A x y) : eq2 A x y :=
match p with eq1_refl _ _ => eq2_refl A x end.
Definition g {A} {x y : A} (p : eq2 A x y) : eq1 A x y :=
match p with eq2_refl _ z => eq1_refl A z end.
Definition fg {A} (x y : A) (p : eq2 A x y) : f (g p) = p :=
match p with eq2_refl _ _ => eq_refl end.
Definition gf {A} (x y : A) (p : eq1 A x y) : g (f p) = p :=
match p with eq1_refl _ _ => eq_refl end.
Although not immediately clear, eq1 corresponds to your PathInduction.eq, and eq2 corresponds to your MartinLof.eq. You can check this by asking Coq to print the types of their recursion principles:
Check eq1_rect.
Check eq2_rect.
You may notice that I have defined the two in Type instead of Prop. I just did this to make the recursion principles generated by Coq closer to the ones you had; by default, Coq uses simpler recursion principles for things defined in Prop (though that behavior can be changed with a few commands).

Idris: arithmetics for bounded Double

I am new to Idris. I need to create a data describing a bounded number. So I've made such data with such a constructor:
data BoundedDouble : (a, b : Double) -> Type where
MkBoundedDouble : (x : Double) ->
{auto p : a <= x && x <= b = True} ->
BoundedDouble a b
It seems to create a Double between a and b.
And here is a simple example of use:
test : BoundedDouble 0.0 1.0
test = MkBoundedDouble 0.0
It works. But now I want to implement Num interface for BoundedDouble. I tried this:
Num (BoundedDouble a b) where
(MkBoundedDouble x) + (MkBoundedDouble y) =
MkBoundedDouble (ifThenElse (x + y > b)
(x + y - (b - a))
(ifThenElse (x + y < a)
(x + y + (b - a))
(x + y)))
But it doesn't work, I guess why, but I can't explain it.
How should I implement the addition?
I don't know exactly what should I do or read to understand it.
There are two problems here. Double arithmetic is defined with primitive functions. Idris can't even proof that a <= b = True -> b <= c = True -> a <= c = True (which, by the way, does not even hold all the time - so this is not Idris' fault.) There is no proof for a <= b = True other then just checking it, what you tried with the ifThenElse.
When working with such blind run-time proofs (so just … = True), Data.So is quite helpful. ifThenElse (a <= x) … … branches off given a boolean check, but the code in the branches does not know about result of the check. With choose (a <= x) you get the result for the branches, with Left prf and prf : So (a <= x) or Right prf and prf : So (not (a <= x)).
I suppose if the result of adding two bounded doubles would be bigger then the upper bound, the result should be this upper bound. Lets make a first attempt:
import Data.So
data BoundedDouble : (a, b : Double) -> Type where
MkBoundedDouble : (x : Double)
-> {auto high : So (a <= x)}
-> {auto low : So (x <= b)}
-> BoundedDouble a b
Num (BoundedDouble a b) where
(+) (MkBoundedDouble u) (MkBoundedDouble v) =
let x = u + v
in case (choose (a <= x), choose (x <= b)) of
(Left _, Left _) => MkBoundedDouble x
(Right _, _) => ?holeMin
(_, Right _) => ?holeMax
This already typechecks, but has holes in it. We want to set ?holeMin to MkBoundedDouble a and ?holeMax to MkBoundedDouble b. However, MkBoundedDouble right now needs two proofs: high and low. In the case of ?holeMax those would be with x = b So (a <= b) and So (b <= b). Again, Idris does not know that b <= b for every b : Double. So we would need to choose again to get these proofs:
(_, Right _) => case (choose (a <= b), choose (b <= b)) of
(Left _, Left _) => MkBoundedDouble b
_ => ?what
Because Idris cannot see that b <= b, the function would be partial. We could cheat and use for example MkBoundedDouble u in ?what, so the function will typecheck and hope that this will indeed never occur.
There is also the possibility to convince the type checker with force that b <= b is always true:
data BoundedDouble : (a, b : Double) -> Type where
MkBoundedDouble : (x : Double)
-> {auto rightSize : So (a <= b)}
-> {auto high : So (a <= x)}
-> {auto low : So (x <= b)}
-> BoundedDouble a b
DoubleEqIsSym : (x : Double) -> So (x <= x)
DoubleEqIsSym x = believe_me (Oh)
Num (BoundedDouble a b) where
(+) (MkBoundedDouble u) (MkBoundedDouble v) =
let x = u + v
in case (choose (a <= x), choose (x <= b)) of
(Left _, Left _) => MkBoundedDouble x
(Right _, _) => MkBoundedDouble a {high=DoubleEqIsSym a}
(_, Right _) => MkBoundedDouble b {low=DoubleEqIsSym b}
Or we could be even safer and put the proofs for the upper and lower bounds in the data constructor, so we can use them in ?holeMin and ?holeMax. This would be:
import Data.So
data BoundedDouble : (a, b : Double) -> Type where
MkBoundedDouble : (x : Double)
-> {auto rightSize : So (a <= b)}
-> {auto leftId : So (a <= a)}
-> {auto rightId : So (b <= b)}
-> {auto high : So (a <= x)}
-> {auto low : So (x <= b)}
-> BoundedDouble a b
Num (BoundedDouble a b) where
(+) (MkBoundedDouble u) (MkBoundedDouble v) =
let x = u + v
in case (choose (a <= x), choose (x <= b)) of
(Left _, Left _) => MkBoundedDouble x
(Right _, _) => MkBoundedDouble a
(_, Right _) => MkBoundedDouble b
You see that even that the constructor is packed with proofs, they don't complicate the implementation. And they should get erased in the actual run-time code.
However, as an exercise you could try to implement Num for
data BoundedDouble : (a, b : Double) -> Type where
MkBoundedDouble : (x : Double)
-> {auto rightSize : So (a <= b)}
-> {auto high : So (a <= x)}
-> {auto low : So (x <= b)}
-> BoundedDouble a b
Min : {auto rightSize : So (a <= b)} -> BoundedDouble a b
Max : {auto rightSize : So (a <= b)} -> BoundedDouble a b
Sadly, there aren't many resources for Idris yet. Besides the tutorial there is a book in development, that I would recommend. It gives more approachable exercises than working with primitive types. :-)

List Equality w/ `cong`

Following my other question, I tried to implement the actual exercise in Type-Driven Development with Idris for same_cons to prove that, given two equal lists, prepending the same element to each list results in two equal lists.
Example:
prove that 1 :: [1,2,3] == 1 :: [1,2,3]
So I came up with the following code that compiles:
sameS : {xs : List a} -> {ys : List a} -> (x: a) -> xs = ys -> x :: xs = x :: ys
sameS {xs} {ys} x prf = cong prf
same_cons : {xs : List a} -> {ys : List a} -> xs = ys -> x :: xs = x :: ys
same_cons prf = sameS _ prf
I can call it via:
> same_cons {x=5} {xs = [1,2,3]} {ys = [1,2,3]} Refl
Refl : [5, 1, 2, 3] = [5, 1, 2, 3]
Regarding the cong function, my understanding is that it takes a proof, i.e. a = b, but I don't understand its second argument: f a.
> :t cong
cong : (a = b) -> f a = f b
Please explain.
If you have two values u : c and v : c, and a function f : c -> d, then if you know that u = v, it has to follow that f u = f v, following simply from referential transparency.
cong is the proof of the above statement.
In this particular use case, you are setting (via unification) c and d to List a, u to xs, v to ys, and f to (:) x, since you want to prove that xs = ys -> (:) x xs = (:) x ys.