Check a function application isn't possible? - testing

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.

Related

Dependent parameters or type-level functions with proofs?

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?

Does it help if proofs are orthogonal?

Suppose I have a function
f : Vect m Nat -> Vect n Nat -> {auto _ : Proof m n} -> Foo m n
where
data Proof : Nat -> Nat -> Type where
Eq : Proof x x
One : Proof 1 _
I can make a Proof 1 1 as Eq or One. Thus these aren't "orthogonal". In more complicated examples, I can have recursive data constructors where I might be able to provide a proof as Constructor1 Constructor2 or Constructor3 Constructor1. Does it matter if constructors aren't orthogonal? In particular, does it hinder proof search?

Plus vs S in a function type

The following declaration of vector cons
cons : a -> Vect n a -> Vect (n + 1) a
cons x xs = x :: xs
fails with the error
Type mismatch between
S n
and
plus n 1
while the following vector append compiles and works
append : Vect n a -> Vect m a -> Vect (n + m) a
append xs ys = xs ++ ys
Why type-level plus is accepted for the second case but not for the first. What's the difference?
Why does x :: xs : Vect (n + 1) a lead to a type error?
(+) is defined by induction on its first argument so n + 1 is stuck (because n is a stuck expression, a variable in this case).
(::) is defined with the type a -> Vect m a -> Vect (S m) a.
So Idris needs to solve the unification problem n + 1 =? S m and because you have a stuck term vs. an expression with a head constructor these two things simply won't unify.
If you had written 1 + n on the other hand, Idris would have reduced that expression down to S n and the unification would have been successful.
Why does xs ++ ys : Vect (n + m) a succeeds?
(++) is defined with the type Vect p a -> Vect q a -> Vect (p + q) a.
Under the assumption that xs : Vect n a and ys : Vect m a, you will have to solve the constraints:
Vect n a ?= Vect p a (xs is the first argument passed to (++))
Vect m a ?= Vect q a (ys is the first argument passed to (++))
Vect (n + m) a ?= Vect (p + q) a (xs ++ ys is the result of append)
The first two constraints lead to n = p and m = q respectively which make the third constraint hold: everything works out.
Consider append : Vect n a -> Vect m a -> Vect (m + n) a.
Notice how I have swapped the two arguments to (+) in this one. You would then be a situation similar to your first question: after a bit of unification, you would end up with the constraint m + n ?= n + m which Idris, not knowing that (+) is commutative, would'nt be able to solve.
Solutions? Work arounds?
Whenever you can it is infinitely more convenient to have a function defined using the same recurrence pattern as the computation that happens in its type. Indeed when that is the case the type will be simplified by computation in the various branches of the function definition.
When you can't, you can rewrite proofs that two things are equal (e.g. that n + 1 = S n for all n) to adjust the mismatch between a term's type and the expected one. Even though this may seem more convenient than refactoring your code to have a different recurrence pattern, and is sometimes necessary, it usually is the start of path full of pitfalls.

Why is filter based on dependent pair?

In the Idris Tutorial a function for filtering vectors is based on dependent pairs.
filter : (a -> Bool) -> Vect n a -> (p ** Vect p a)
filter f [] = (_ ** [])
filter f (x :: xs) with (filter f xs )
| (_ ** xs') = if (f x) then (_ ** x :: xs') else (_ ** xs')
But why is it necessary to put this in terms of a dependent pair instead of something more direct such as?
filter' : (a -> Bool) -> Vect n a -> Vect p a
In both cases the type of p must be determined, but in my supposed alternative the redundancy of listing p twice is eliminated.
My naive attempts at implementing filter' failed, so I was wondering is there a fundamental reason that it can't be implemented? Or can filter' be implemented, and perhaps filter was just a poor example to showcase dependent pairs in Idris? But if that is the case then in what situations would dependent pairs be useful?
Thanks!
The difference between filter and filter' is between existential and universal quantification. If (a -> Bool) -> Vect n a -> Vect p a was the correct type for filter, that would mean filter returns a Vector of length p and the caller can specify what p should be.
Kim Stebel's answer is right on the money. Let me just note that this was already discussed on the Idris mailing list back in 2012 (!!):
filter for vector, a question - Idris Programming Language
What raichoo posted there can help clarifying it I think; the real signature of your filter' is
filter' : {p : Nat} -> {n: Nat} -> {a: Type} -> (a -> Bool) -> Vect a n -> Vect a p
from which it should be obvious that this is not what filter should (or even could) do; p actually depends on the predicate and the vector you are filtering, and you can (actually need to) express this using a dependent pair. Note that in the pair (p ** Vect p a), p (and thus Vect p a) implicitly depends on the (unnamed) predicate and vector appearing before it in its signature.
Expanding on this, why a dependent pair? You want to return a vector, but there's no "Vector with unknown length" type; you need a length value for obtaining a Vector type. But then you can just think "OK, I will return a Nat together with a vector with that length". The type of this pair is, unsurprisingly, an example of a dependent pair. In more detail, a dependent pair DPair a P is a type built out of
A type a
A function P: a -> Type
A value of that type DPair a P is a pair of values
x: a
y: P a
At this point I think that is just syntax what might be misleading you. The type p ** Vect p a is DPair Nat (\p => Vect p a); p there is not a parameter for filter or anything like it. All this can be a bit confusing at first; if so, maybe it helps thinking of p ** Vect p a as a substitute for the "Vector with unknown length" type.
Not an answer, but additional context
Idris 1 documentation - https://docs.idris-lang.org/en/latest/tutorial/typesfuns.html#dependent-pairs
Idris 2 documentation - https://idris2.readthedocs.io/en/latest/tutorial/typesfuns.html?highlight=dependent#dependent-pairs
In Idris 2 the dependent pair defined here
and is similar to Exists and Subset but BOTH of it's values are NOT erased at runtime

Idris - Computation on types based on decidable property doesn't typecheck

I'm facing a problem in Idris, where I want to create a type-level "check" based on a decidable property, where if the property holds I get the type I want, but if the property fails I get Unit (()), signaling that the program is in an inconsistent state (and should not compile if I decide to use it as my original type).
Here's an example:
TestType : {k : Nat} -> Nat -> Vect k Nat -> Type
TestType n ls with (isElem n ls)
TestType n ls | Yes _ = Nat
TestType n ls | No _ = ()
mkTestTypeFromNat : (n : Nat) -> (ls : Vect k Nat) -> Elem n ls -> Nat -> TestType {k=k} n ls
mkTestTypeFromNat n ls prf res = res
mkTestTypeFromUnit : (n : Nat) -> (ls : Vect k Nat) -> Not (Elem n ls) -> TestType {k=k} n ls
mkTestTypeFromUnit n ls prf = ()
When I try to compile this, it shows me the following errors:
When checking right hand side of mkTestTypeFromNat:
Type mismatch between
Nat (Type of n)
and
with block in Extensible_Records.TestType n
k
ls
(isElem n ls) (Expected type)
When checking right hand side of mkTestTypeFromUnit:
Type mismatch between
() (Type of ())
and
with block in Extensible_Records.TestType n
k
ls
(isElem n ls) (Expected type)
In each of these mkTestTypeFrom_ functions I provide the proof of the deciable property, either a proof the element is in the list or the proof it isn't. Shouldn't the typechecker realize it has these proofs, and be able to compute the with (isElem n ls) section without problems, giving me the correct type in each case? Or am I missing something to convince the typechecker of such?
Just having evidence around doesn't really help you until you use it. This is the best I could come up with for your types; I don't know if the code will be optimized sensibly, or if there's a way to make them efficient if not. The compiler will surely recognize that each case analysis has only one reachable branch. What's less clear to me is whether it will realize that it therefore has no need to evaluate the scrutinee.
mkTestTypeFromNat : (n : Nat) -> (ls : Vect k Nat) -> Elem n ls -> Nat -> TestType {k=k} n ls
mkTestTypeFromNat n ls prf res with (isElem n ls)
mkTestTypeFromNat n ls prf res | (Yes x) = res
mkTestTypeFromNat n ls prf res | (No contra) = absurd (contra prf)
mkTestTypeFromUnit : (n : Nat) -> (ls : Vect k Nat) -> Not (Elem n ls) -> TestType {k=k} n ls
mkTestTypeFromUnit n ls prf with (isElem n ls)
mkTestTypeFromUnit n ls prf | (Yes x) = absurd (prf x)
mkTestTypeFromUnit n ls prf | (No contra) = ()