Proof inside nested constructor of recursive type - idris

I am having some trouble with the code below. Essentially I want to create a slice type. The motivation comes from Python, where a slice is [start:end:step], used for slicing a sublist out of a list. This is conceptually the same as a sequence of indices [start, start+step, start+2*step, ..., end].
The way I've tried to capture it is Slice n can be applied to a Vect (n+m) a. The basic constructor FwdS will create a slice with a non-zero step (proof stepNZ). The SLen constructor will increment an existing slice's Vect size requirement by its step (computed using stepOf). Similarly SStart increments a slice's Vect size requirement by 1.
Then the final value conceptually corresponds to:
start := # of SStart in slice
stop := start + (# of SLen) * step
step := constructor argument in FwdS
If the slice is [start:stop:step].
mutual
data Slice : Nat -> Type where
FwdS : (step : Nat) -> {stepNZ : Not (step = Z)} -> Slice Z
SLen : (x : Slice len) -> Slice (len + (stepOf x))
SStart : Slice len -> Slice (S len)
stepOf : Slice n -> Nat
stepOf (FwdS step) = step
stepOf (SLen slice) = stepOf slice
stepOf (SStart slice) = stepOf slice
length : Slice n -> Nat
length (FwdS step ) = Z
length (SLen slice) = let step = stepOf slice
len = length slice
in len + step
length (SStart slice) = length slice
select : (slice: Slice n) -> Vect (n+m) a -> Vect (length slice) a
select (FwdS step) xs = []
select (SStart slice) (x :: xs) = select slice xs
select (SLen slice) (xs) = ?trouble
The trouble is in the last pattern. I'm not sure what the issue is - if I try to case split on xs I get both [] and (_::_) impossible. Ideally I'd like to have that case read something like this:
select (SLen slice) (x :: xs) = let rec = drop (stepOf slice) (x::xs)
in x :: (select slice rec)
and have Idris recognize that if the first argument is an SLen constructor, the second argument cannot be []. My intuition is that at the SLen level, Idris does not understand it already has a proof that stepOf slice is not Z. But I'm not sure how to test that idea.

My intuition is that at the SLen level, Idris does not understand it already has a proof that stepOf slice is not Z.
You are right. With :t trouble you see that the compiler doesn't have enough information to infer that (plus (plus len (stepOf slice)) m) is not 0.
a : Type
m : Nat
len : Nat
slice : Slice len
xs : Vect (plus (plus len (stepOf slice)) m) a
--------------------------------------
trouble : Vect (plus (length slice) (stepOf slice)) a
You would have to solve two problems: get a proof that stepOf slice is S k for some k like getPrf : (x : Slice n) -> (k ** stepOf x = (S k)), then rewrite Vect (plus (plus len (stepOf slice)) m) a to something like Vect (S (plus k (plus len m))) a so the compiler can at least know that this xs is not empty. But it doesn't get easier from thereon. :-)
Basically whenever you have a function where you use functions in the arguments, you probably can rewrite those informations into the type. Like select with length slice or SLen with stepOf x. Here is an example implementation:
data Slice : (start : Nat) -> (len : Nat) -> (step : Nat) -> (cnt : Nat) -> Type where
FwdS : (step : Nat) -> Slice Z Z step Z
SLen : Slice Z len step cnt -> Slice Z (S step + len) step (S cnt)
SStart : Slice start len step cnt -> Slice (S start) len step cnt
You gain a lot from this: you can access the parameters len and step directly without proving the functions length and stepOf first. Also you can have better control about your allowed data. For example, in your definiton SLen $ SStart $ SLen $ SStart $ FwdS 3 would have been valid, mixing steps and start increments.
select could look like this:
select : Slice start len step cnt -> Vect (start + len + m) a -> Vect cnt a
select (FwdS k) xs = []
select (SStart s) (x :: xs) = select s xs
select (SLen s) [] impossible
select (SLen s {step} {len} {cnt}) (x::xs) {m} {a} =
let is = replace (sym $ plusAssociative step len m) xs {P=\t => Vect t a} in
(x :: select s (drop step is))
If you want an exercise with proving, you could try to implement select : Slice start len step cnt -> Vect (len + start + m) a -> Vect cnt a, so switching start + len.
And getting two elements starting at 1 in steps of 4:
> select (SStart $ SLen $ SLen $ FwdS 3) [0,1,2,3,4,5,6,7,8,9]
[1, 5] : Vect 2 Integer

Related

Why does Idris give me Type mismatch error for the following code?

I am a newbie trying to learn Idris and wanted to create a function test that returns a Vector whose type is parameterized by the function argument.
vecreplicate : (len : Nat) -> (x : elem) -> Vect len elem
vecreplicate Z x = []
vecreplicate (S k) x = x :: vecreplicate k x
test : (k:Nat) -> Nat -> Vect k Int
test Z = vecreplicate Z (toIntegerNat Z)
test k = vecreplicate k (toIntegerNat k)
When I try to compile the code with Idris I get the following type error
Type mismatch between
Vect len elem (Type of vecreplicate len x)
and
Nat -> Vect 0 Int (Expected type)
Specifically:
Type mismatch between
Vect len
and
\uv => Nat -> uv
Why am I getting this error?
test : (k:Nat) -> Nat -> Vect k Int takes two arguments, but you only match on k. That's why the expected type is a lambda (Nat -> Vect 0 Int). Just drop one Nat: test : (k : Nat) -> Vect k Int.
Also, toIntegerNat returns Integer not Int. :search Nat -> Int returns toIntNat, so that's what you want to use there.

Idris Vect.fromList usage with generated list

I am trying to feel my way into dependent types. Based on the logic of the windowl function below, I want to return a list of vectors whose length depend on the size provided.
window : (n : Nat) -> List a -> List (Vect n a)
window size = map fromList loop
where
loop xs = case splitAt size xs of
(ys, []) => if length ys == size then [ys] else []
(ys, _) => ys :: loop (drop 1 xs)
windowl : Nat -> List a -> List (List a)
windowl size = loop
where
loop xs = case List.splitAt size xs of
(ys, []) => if length ys == size then [ys] else []
(ys, _) => ys :: loop (drop 1 xs)
When I attempt to load the function into Idris, I get the following:
When checking argument func to function Prelude.Functor.map:
Type mismatch between
(l : List elem) -> Vect (length l) elem (Type of fromList)
and
a1 -> List (Vect size a) (Expected type)
Specifically:
Type mismatch between
Vect (length v0) elem
and
List (Vect size a)
When reading the documentation on fromList I notice that it says
The length of the list should be statically known.
So I assume that the type error has to do with Idris not knowing that the length of the list is corresponding to the size specified.
I am stuck because I don't even know if it is something impossible I want to do or whether I can specify that the length of the list corresponds to the length of the vector that I want to produce.
Is there a way to do that?
Since in your case it is not possible to know the length statically, we need a function which can fail at run-time:
total
fromListOfLength : (n : Nat) -> (xs : List a) -> Maybe (Vect n a)
fromListOfLength n xs with (decEq (length xs) n)
fromListOfLength n xs | (Yes prf) = rewrite (sym prf) in Just (fromList xs)
fromListOfLength n xs | (No _) = Nothing
fromListOfLength converts a list of length n into a vector of length n or fails. Now let's combine it and windowl to get to window.
total
window : (n : Nat) -> List a -> List (Vect n a)
window n = catMaybes . map (fromListOfLength n) . windowl n
Observe that the window function's type is still an underspecification of what we are doing with the input list, because nothing prevents us from always returning the empty list (this could happen if fromListOfLength returned Nothing all the time).

Check if Vector's Lengths are Equal

Given the following from Type-Driven Development with Idris:
import Data.Vect
data EqNat : (num1 : Nat) -> (num2 : Nat) -> Type where
Same : (num : Nat) -> EqNat num num
sameS : (eq : EqNat k j) -> EqNat (S k) (S j)
sameS (Same n) = Same (S n)
checkEqNat : (num1 : Nat) -> (num2 : Nat) -> Maybe (EqNat num1 num2)
checkEqNat Z Z = Just $ Same Z
checkEqNat Z (S k) = Nothing
checkEqNat (S k) Z = Nothing
checkEqNat (S k) (S j) = case checkEqNat k j of
Just eq => Just $ sameS eq
Nothing => Nothing
exactLength : (len : Nat) -> (input : Vect m a) -> Maybe (Vect len a)
exactLength {m} len input = case (checkEqNat m len) of
Just (Same m) => Just input
Nothing => Nothing
If I replace the last function's Just (Same m) with Just eq, the compiler complains:
*Lecture> :r
Type checking ./Lecture.idr
Lecture.idr:19:75:
When checking right hand side of Main.case block in exactLength at Lecture.idr:18:34 with expected type
Maybe (Vect len a)
When checking argument x to constructor Prelude.Maybe.Just:
Type mismatch between
Vect m a (Type of input)
and
Vect len a (Expected type)
Specifically:
Type mismatch between
m
and
len
Holes: Main.exactLength
How does Just (Same m), i.e. the working code, provide "evidence" that exactLength's len and m are equal?
What I find useful when working with Idris is adding holes when you're not sure about something rather than solving them. Like adding a hole into Just ... branch to see what's going on there:
exactLength : (len : Nat) -> (input : Vect m a) -> Maybe (Vect len a)
exactLength {m} len input = case (checkEqNat m len) of
Just (Same m) => ?hole
Nothing => Nothing
and then change (Same m) to eq and back while looking at the results of type checking. In the eq case it's like this:
- + Main.hole [P]
`-- a : Type
m : Nat
len : Nat
eq : EqNat m len
input : Vect m a
--------------------------------
Main.hole : Maybe (Vect len a)
And in the (Same m) case it's like this:
- + Main.hole_1 [P]
`-- m : Nat
a : Type
input : Vect m a
--------------------------------
Main.hole_1 : Maybe (Vect m a)
So eq is something of a type EqNat m len, no one knows whether it's inhabitant or not, while Same m (or Same len) is definitely inhabitant which proves that m and len are equal.
When you start with
exactLength : (len : Nat) -> (input : Vect m a) -> Maybe (Vect len a)
exactLength {m} len input with (_)
exactLength {m} len input | with_pat = ?_rhs
and gradually extend the missing links until you reached
exactLength : (len : Nat) -> (input : Vect m a) -> Maybe (Vect len a)
exactLength {m} len input with (checkEqNat m len)
exactLength {m = m} len input | Nothing = Nothing
exactLength {m = len} len input | (Just (Same len)) = Just input
you can see how idris can derive from the fact that checkEqNat m len returned a Just (Same ...) that it can then infer that {m = len}. AFAIK just writing Just eq is not a proof that eq is indeed inhabited.

Concatenation of two vectors - why are lengths not treated as commutative?

I'm playing around with Idris and I came across something which confuses me a little:
The following type checks:
conc : Vect n a -> Vect m a -> Vect (n+m) a
conc [] ys = ys
conc (x :: xs) ys = x :: (conc xs ys)
but this doesn't:
conc : Vect n a -> Vect m a -> Vect (m+n) a
conc [] ys = ys
conc (x :: xs) ys = x :: (conc xs ys)
with the following error:
When checking right hand side of conc with expected type
Vect (m + 0) a
Type mismatch between
Vect m a (Type of ys)
and
Vect (plus m 0) a (Expected type)
Specifically:
Type mismatch between
m
and
plus m 0
Equivalently xs++ys type checks but ys++xs doesn't even though they would both match the type definition of having length n+m.
This surprises me a little as addition is commutative. Is there anything I can do (perhaps with constraints?) to the function signature to get both xs++ys and ys++xs expressions type checking?
This is a common topic of confusion for beginners in Idris.
The problem in this case in second conc version:
conc : Vect n a -> Vect m a -> Vect (m+n) a
conc [] ys = ys
Idris cannot apply addition commutativity because Nat plus commutativity is a theorem from the compiler point of view. Here its type:
Idris> :t plusCommutative
plusCommutative : (left : Nat) -> (right : Nat) -> left + right = right + left
There're no general rules to tell you which theorem to choose and to apply. Of course, some heuristics can be made for some simple cases, like Nat commutativity. But this also can make difficult to understand some other cases.
The other thing you need to consider is definition of plus:
Idris> :printdef plus
plus : Nat -> Nat -> Nat
plus 0 right = right
plus (S left) right = S (plus left right)
Function plus defined in a such way that is does pattern matching on its first argument. Idris actually can perform this pattern matching in types. So in first version, where
conc : Vect n a -> Vect m a -> Vect (n+m) a
conc [] ys = ys
you have (0 + m) in type and Idris can replace plus 0 m with m and everything typechecks. plus m 0 would work if you define your + operator by pattern matching on second argument. For example, this compiles:
infixl 4 +.
(+.) : Nat -> Nat -> Nat
n +. Z = n
n +. (S m) = S (n +. m)
conc : Vect n a -> Vect m a -> Vect (m +. n) a
conc [] ys = ys
conc (x :: xs) ys = x :: (conc xs ys)
But it's obvious that writing new operator for every case you need is horrible. So to make your second version compilable you should use rewrite ... in syntax in Idris. You need next theorems:
Idris> :t plusZeroRightNeutral
plusZeroRightNeutral : (left : Nat) -> left + 0 = left
Idris> :t plusSuccRightSucc
plusSuccRightSucc : (left : Nat) -> (right : Nat) -> S (left + right) = left + S right
Idris> :t sym
sym : (left = right) -> right = left
And here is version which compiles:
conc : Vect n a -> Vect m a -> Vect (m + n) a
conc {m} [] ys = rewrite plusZeroRightNeutral m in ys
conc {n=S k} {m} (x :: xs) ys = rewrite (sym $ plusSuccRightSucc m k) in x :: (conc xs ys)
I'm not explaining here how rewriting and theorem proving works here. This is a topic of another question if you don't understand something. But you can read about that in tutorial (or wait for The Book release :) .

(xs : Vect n elem) -> Vect (n * 2) elem

The book Type Driven Development with Idris presents this exercise:
Define a possible method that fits the signature:
two : (xs : Vect n elem) -> Vect (n * 2) elem
I tried:
two : (xs : Vect n elem) -> Vect (n * 2) elem
two xs = xs ++ xs
But I got the following error:
*One> :r
Type checking ./One.idr
One.idr:9:5:When checking right hand side of two:
Type mismatch between
Vect (n + n) elem (Type of xs ++ xs)
and
Vect (mult n 2) elem (Expected type)
Specifically:
Type mismatch between
plus n n
and
mult n 2
Holes: Hw1.two
If I have a Vector of size N, and need a Vector of size N*2, then appending it to itself seems reasonable.
What am I doing wrong?
Short answer
Change the type signature to two : (xs : Vect n elem) -> Vect (n + n) elem.
If you really need it that way
Getting to Vect (n * 2) elem is a bit complicated. Here:
two' : Vect n elem -> Vect (n * 2) elem
two' {n} xs = rewrite multCommutative n 2 in rewrite plusZeroRightNeutral n in xs ++ xs
The reason you got that error message is that equality in typechecking is equality after reduction to normal form. n + n and mult n 2 are equal, but their normal forms aren't. (mult n 2 is what n * 2 reduces to after resolving the typeclass.)
You can see the definition of mult like so:
*kevinmeredith> :printdef mult
mult : Nat -> Nat -> Nat
mult 0 right = 0
mult (S left) right = plus right (mult left right)
It works by pattern matching on the first argument. Since the first argument in the type signature of two is n, mult can't be reduced at all. multCommutative will help us flip it around:
*kevinmeredith> :t multCommutative
multCommutative : (left : Nat) ->
(right : Nat) -> left * right = right * left
Our best tool to apply that equality is rewrite, like in my definition of two'. (run :t replace at the REPL if you want to see how to do it the hard way) In the rewrite foo in bar construction, foo is something of type a = b and bar has the type of the outer expression, but with all as replaced by bs. In my two' above, I first used it to change Vect (n * 2) to Vect (2 * n). This lets mult reduce. If we look at mult above, and apply it to 2 i.e. S (S Z) and n, you get plus n (mult (S Z) n, and then plus n (plus n (mult Z n)), and then plus n (plus n Z). You don't have to work out the reduction yourself, you can just apply the rewrite and put a hole on the end:
two' : Vect n elem -> Vect (n * 2) elem
two' {n} xs = rewrite multCommutative n 2 in ?aaa
Then ask Idris:
*kevinmeredith> :t aaa
elem : Type
n : Nat
xs : Vect n elem
_rewrite_rule : plus n (plus n 0) = mult n 2
--------------------------------------
aaa : Vect (plus n (plus n 0)) elem
plus n Z doesn't reduce because plus is defined by recursion on its first argument, just like mult. plusZeroRightNeutral gets you the equality you need:
*kevinmeredith> :t plusZeroRightNeutral
plusZeroRightNeutral : (left : Nat) -> left + 0 = left
I used the same technique with rewrite again.
:search will let you search the library for inhabitants of a given type. You'll often find someone has done the work of proving things for you.
*kevinmeredith> :s (n : Nat) -> n + 0 = n
= Prelude.Nat.multOneLeftNeutral : (right : Nat) ->
fromInteger 1 * right = right
= Prelude.Nat.plusZeroRightNeutral : (left : Nat) ->
left + fromInteger 0 = left
*kevinmeredith> :s (n, m : Nat) -> n * m = m * n
= Prelude.Nat.multCommutative : (left : Nat) ->
(right : Nat) -> left * right = right * left
(This answer is for Idris version 0.9.20.1)