In this example
%hint
lemma1: S a `LTE` S b -> a `LTE` b
lemma1 = ?todo1
myMinus: (a, b: Nat) -> {auto prf: b `LTE` a} -> Nat
myMinus (S a') (S b') = a' `myMinus` b' -- automatically uses lemma1
myMinus a b = ?todo2
Idris is able to automatically use lemma1 where it's needed. Now I add a second lemma
%hint
lemma2: S a `LTE` b -> a `LTE` b
lemma2 = ?todo3
between lemma1 and myMinus. Now Idris doesn't find lemma1 anymore, probably because it only tries lemma2, and I have to specify it manually.
Is there a way, that I can have both lemma1 and lemma2 with %hint in context and let Idris automatically choose the correct one?
Related
I'm deciding between parametrising my type by a Vect r Nat and List Nat. At first, I thought that with the former I can constrain the vector length like
foo : Vect m Nat -> Vect m Nat -> Vect (2 * m) Nat
and this makes Vect more powerful, but then I realised I can do the same, albeit more verbosely, with lists and proofs
foo : (x : List Nat) -> (y : List Nat)
-> {auto _ : length x = length y} -> List (2 * length x) Nat
Now I'm wondering if this is generally true. Is parametrising with values any different to using type-level functions with proofs?
I'd like to check my function signatures mean what I think they mean. I can of course check the positive variants
test_concat : Vect [3] Nat -> Vect [2] Nat -> Vect [5] Nat
test_concat x y = x ++ y
but I'd like to test some negative variants, sth like
test_concat_invalid : Vect [3] Nat -> Vect [2] Nat -> Vect [1] Nat -> Void
test_concat_invalid x y = x ++ y impossible
though obviously the syntax is made up. I've found I can do sth similar to test if I've written a data constructor for proofs correctly. Is this possible?
You can create a type which is indexed by a separate type and value:
data Typed : Type -> a -> Type where
MkTyped : {A : Type} -> {x : A} -> Typed A x
But only provide a constructor which links the type index with the value index - i.e. Typed can only be constructed if the value x has the type A.
This lets us write a proposition like so:
test_concat_invalid
: (x : Vect 3 Nat)
-> (y : Vect 2 Nat)
-> Typed (Vect 1 Nat) (x ++ y)
-> Void
test_concat_invalid _ _ MkTyped impossible
When Idris tries to unify the types, it sees 1 is not 5 which are obviously different, by construction. This means Idris will happily accept that the MkTyped constructor is impossible in this proposition.
The following proves the equality of two functions:
η-→ : ∀ {A B : Set} (f : A → B) → (λ (x : A) → f x) ≡ f
η-→ f = refl
Why doesn't it need extensionality? How does Agda know that the function to the left of the ≡ simplifies to f?
(λ x → f x) ≡ f is a basic rule of definitional equality for functions, called the eta rule. It's built into the type checker. Implementations of type theory commonly support it.
I am just starting to learn Idris, and I figured a nice little project to start off would be to implement finite sequences as 2-3 finger trees. Each internal node in a tree needs to be annotated at run time with the total number of elements stored below it in order to support fast splitting and indexing. This size information also needs to be managed at compile time in order to (eventually) prove that splitting with appropriate indices and zipping a sequence with another sequence are total operations.
I can think of two ways to deal with this:
What I'm currently doing, having written a tiny fraction of the total necessary code: handle the sizes entirely in the types, and then use something like proof {intros; exact s;} to get at them. I don't know what, if any, horrible efficiency consequences this might have. Among potentials in my mind: a) Unnecessarily storing a size with each leaf node. b) I think this unlikely, but it would be real bad if it insisted on calculating sizes from the bottom up rather than lazily from the top down.
Include an explicit size field in each node constructor, along with a proof that the number in the size field matches the size the type system requires. This approach seems extremely awkward. On the plus side, I should be able to be quite certain that the type-level numbers and the equality proofs get erased, leaving just one number per internal node at run time.
Which of these, if either, is the right way to go?
The current code
Please feel free to give style tips, and maybe to explain how to inline the size code. I could only figure out what to do interactively, but it seems a bit weird to have proofs at the bottom for such simple things.
data Tree23 : Nat -> Nat -> Type -> Type where
Elem : a -> Tree23 0 1 a
Node2 : Lazy (Tree23 d s1 a) -> Lazy (Tree23 d s2 a) ->
Tree23 (S d) (s1 + s2) a
Node3 : Lazy (Tree23 d s1 a) -> Lazy (Tree23 d s2 a) -> Lazy (Tree23 d s3 a) ->
Tree23 (S d) (s1 + s2 + s3) a
size23 : Tree23 d s a -> Nat
size23 t = ?size23RHS
data Digit : Nat -> Nat -> Type -> Type where
One : Lazy (Tree23 d s a) -> Digit d s a
Two : Lazy (Tree23 d s1 a) -> Lazy (Tree23 d s2 a) -> Digit d (s1+s2) a
Three : Lazy (Tree23 d s1 a) -> Lazy (Tree23 d s2 a) ->
Lazy (Tree23 d s3 a) -> Digit d (s1+s2+s3) a
Four : Lazy (Tree23 d s1 a) -> Lazy (Tree23 d s2 a) ->
Lazy (Tree23 d s3 a) -> Lazy (Tree23 d s4 a) -> Digit d (s1+s2+s3+s4) a
sizeDig : Digit d s a -> Nat
sizeDig t = ?sizeDigRHS
data FingerTree : Nat -> Nat -> Type -> Type where
Empty : FingerTree d 0 a
Single : Tree23 d s a -> FingerTree d s a
Deep : Digit d spr a -> Lazy (FingerTree (S d) sm a) -> Digit d ssf a ->
FingerTree d (spr + sm + ssf) a
data Seq' : Nat -> Type -> Type where
MkSeq' : FingerTree 0 n a -> Seq' n a
Seq : Type -> Type
Seq a = (n ** Seq' n a)
---------- Proofs ----------
try.sizeDigRHS = proof
intros
exact s
try.size23RHS = proof
intros
exact s
Edit
Another option I've explored a bit is to try to separate the data structure from its validity. This leads to the following:
data Tree23 : Nat -> Type -> Type where
Elem : a -> Tree23 0 a
Node2 : Nat -> Lazy (Tree23 d a) -> Lazy (Tree23 d a) ->
Tree23 (S d) a
Node3 : Nat -> Lazy (Tree23 d a) -> Lazy (Tree23 d a) -> Lazy (Tree23 d a) ->
Tree23 (S d) a
size23 : Tree23 d a -> Nat
size23 (Elem x) = 1
size23 (Node2 s _ _) = s
size23 (Node3 s _ _ _) = s
data Valid23 : Tree23 d a -> Type where
ElemValid : Valid23 (Elem x)
Node2Valid : Valid23 x -> Valid23 y -> Valid23 (Node2 (size23 x + size23 y) x y)
Node3Valid : Valid23 x -> Valid23 y -> Valid23 z
-> Valid23 (Node3 (size23 x + size23 y + size23 z) x y z)
data Digit : Nat -> Type -> Type where
One : Lazy (Tree23 d a) -> Digit d a
Two : Lazy (Tree23 d a) -> Lazy (Tree23 d a) -> Digit d a
Three : Lazy (Tree23 d a) -> Lazy (Tree23 d a) ->
Lazy (Tree23 d a) -> Digit d a
Four : Lazy (Tree23 d a) -> Lazy (Tree23 d a) ->
Lazy (Tree23 d a) -> Lazy (Tree23 d a) -> Digit d a
data ValidDig : Digit d a -> Type where
OneValid : Valid23 x -> ValidDig (One x)
TwoValid : Valid23 x -> Valid23 y -> ValidDig (Two x y)
ThreeValid : Valid23 x -> Valid23 y -> Valid23 z -> ValidDig (Three x y z)
FourValid : Valid23 x -> Valid23 y -> Valid23 z -> Valid23 w -> ValidDig (Four x y z w)
sizeDig : Digit d a -> Nat
sizeDig (One x) = size23 x
sizeDig (Two x y) = size23 x + size23 y
sizeDig (Three x y z) = size23 x + size23 y + size23 z
sizeDig (Four x y z w) = (size23 x + size23 y) + (size23 z + size23 w)
data FingerTree : Nat -> Type -> Type where
Empty : FingerTree d a
Single : Tree23 d a -> FingerTree d a
Deep : Nat -> Digit d a -> Lazy (FingerTree (S d) a) -> Digit d a ->
FingerTree d a
sizeFT : FingerTree d a -> Nat
sizeFT Empty = 0
sizeFT (Single x) = size23 x
sizeFT (Deep k x y z) = k
data ValidFT : FingerTree d a -> Type where
ValidEmpty : ValidFT Empty
ValidSingle : Valid23 x -> ValidFT (Single x)
ValidDeep : ValidDig pr -> ValidFT m -> ValidDig sf ->
ValidFT (Deep (sizeDig pr + sizeFT m + sizeDig sf) pr m sf)
record Seq : Type -> Type where
MkSeq : FingerTree 0 a -> Seq a
data ValidSeq : Seq a -> Type where
MkValidSeq : ValidFT t -> ValidSeq (MkSeq t)
Then each function is accompanied by a (separate) proof of its validity.
I kind of like the way this approach separates "code" from "proofs", but I've run into a couple problems with it:
While the "code" gets easier, the proofs seem to get a good bit harder to construct. I imagine a large part of that is probably a result of my not being familiar with the system.
I haven't actually gotten near the point where I will write this code, but the indexing, splitting, and zipping functions will all have to insist on getting a proof of the validity of their input(s). It seems a bit weird for some functions to work with just sequences, and others to insist on proofs, but maybe that's just me.
Your problem can be simplified to having a type like
data Steps : Nat -> Type where
Nil : Steps 0
Cons : Steps n -> Steps (S n)
and wanting to write
size : Steps n -> Nat
This is very easy to do since the implicitly-quantified arguments (n in this case) are passed to size as implicit arguments! So the above type of size is the same as
size : {n : _} -> Steps n -> Nat
which means it can be defined as
size {n} _ = n
I have a simple lemma:
Lemma map2_comm: forall A (f:A->A->B) n (a b:t A n),
(forall x y, (f x y) = (f y x)) -> map2 f a b = map2 f b a.
which I was able to prove using standard equality (≡). Now I am need to prove the similar lemma using setoid equality (using CoRN MathClasses). I am new to this library and type classes in general and having difficulty doing so. My first attempt is:
Lemma map2_setoid_comm `{Equiv B} `{Equiv (t B n)} `{Commutative B A}:
forall (a b: t A n),
map2 f a b = map2 f b a.
Proof.
intros.
induction n.
dep_destruct a.
dep_destruct b.
simpl.
(here '=' is 'equiv'). After 'simpl' the goal is "(nil B)=(nil B)" or "[]=[]" using VectorNotations. Normally I would finish it using 'reflexivity' tactics but it gives me:
Tactic failure: The relation equiv is not a declared reflexive relation. Maybe you need to require the Setoid library.
I guess I need somehow to define reflexivity for vector types, but I am not sure how to do that. Please advise.
First of all the lemma definition needs to be adjusted to:
Lemma map2_setoid_comm : forall `{CO:Commutative B A f} `{SB: !Setoid B} ,
forall n:nat, Commutative (map2 f (n:=n)).
To be able to use reflexivity:
Definition vec_equiv `{Equiv A} {n}: relation (vector A n) := Vforall2 (n:=n) equiv.
Instance vec_Equiv `{Equiv A} {n}: Equiv (vector A n) := vec_equiv.