I'm working from the TDD with idris book using Idris2, and in Chapter 6 we write a function that adds a dynamic amount of numbers to each other. This is my impl with only Ints:
AdderType : (numargs : Nat) -> Type
AdderType Z = Int
AdderType (S k) = Int -> AdderType k
adder : (numargs : Nat) -> Int -> AdderType numargs
adder Z acc = acc
adder (S k) acc = \next => adder k (next + acc)
Note that the (S k) case of adder returns a lambda. I tried to just add next to the arguments of adder:
adder : (numargs : Nat) -> Int -> AdderType numargs
adder Z acc = acc
adder (S k) acc next = adder k (next + acc)
But when I try to compile this idris2 gives me the following error.
Error:
While processing right hand side of adder.
Trying to use linear name next in non-linear context.
24 | adder : (numargs : Nat) -> Int -> AdderType numargs
25 | adder 0 acc = acc
26 | adder (S k) acc next = adder k (next + acc)
^^^^
Why does idris2 decide that next is linear in this case without me explicitly saying it is? Is this a bug?
Note: If I do the same for the more generic impl listed in the book:
AdderType : (numargs : Nat) -> Type -> Type
AdderType Z numType = numType
AdderType (S k) numType = (next : numType) -> AdderType k numType
adder : Num numType => (numargs : Nat) -> numType -> AdderType numargs numType
adder Z acc = acc
adder (S k) acc next = adder k (next + acc)
I get the following error:
Error: While processing left hand side of adder. Unsolved holes:
Tri.{argTy:4060} introduced at:
/home/stefan/dev/tdd-idris/Tri.idr:25:1--25:6
21 | AdderType (S k) numType = (next : numType) -> AdderType k numType
22 |
23 | adder : Num numType => (numargs : Nat) -> numType -> AdderType numargs numType
24 | adder Z acc = acc
25 | adder (S k) acc next = adder k (next + acc)
^^^^^
Related
I'm a green hand with Idris,and get confused with this definition, as I don't understand how it works.
The definitionare as follows.
sameS : (k : Nat)->(j : Nat)->(k = j)->((S k) = (S j))
sameS x x Refl=Refl
Let us start by breaking down the type signature:
sameS : (k : Nat) -> (j : Nat) -> (k = j) -> ((S k) = (S j))
sameS is a function.
sameS take the following arguments:
(k : Nat) a parameter k of type Nat
(j : Nat) a parameter j of type Nat
(k = j) A proof that k and j are equal
sameS returns:
((S k) = (S j)) proof that S k and S j are equal.
Now let us breakdown the definition:
sameS x x Refl = Refl
The type of Refl is a = a.
x is both the first and second argument because both are identical. We know this because the 3rd argument is Refl.
Refl is returned because S x = S x.
Suppose we'd like to have a "proper" minus on Nats, requiring m <= n for n `minus` m to make sense:
%hide minus
minus : (n, m : Nat) -> { auto prf : m `LTE` n } -> Nat
minus { prf = LTEZero } n Z = n
minus { prf = LTESucc prevPrf } (S n) (S m) = minus n m
Now let's try to prove the following lemma, stating that (n + (1 + m)) - k = ((1 + n) + m) - k, assuming both sides are valid:
minusPlusTossS : (n, m, k : Nat) ->
{ auto prf1 : k `LTE` n + S m } ->
{ auto prf2 : k `LTE` S n + m } ->
minus (n + S m) k = minus (S n + m) k
The goal suggests the following sublemma might help:
plusTossS : (n, m : Nat) -> n + S m = S n + m
plusTossS Z m = Refl
plusTossS (S n) m = cong $ plusTossS n m
so we try to use it:
minusPlusTossS n m k =
let tossPrf = plusTossS n m
in rewrite tossPrf in ?rhs
And here we fail:
When checking right hand side of minusPlusTossS with expected type
minus (n + S m) k = minus (S n + m) k
When checking argument prf to function Main.minus:
Type mismatch between
LTE k (S n + m) (Type of prf2)
and
LTE k replaced (Expected type)
Specifically:
Type mismatch between
S (plus n m)
and
replaced
If I understand this error correctly, it just means that it tries to rewrite the RHS of the target equality (which is minus { prf = prf2 } (S n + m) k) to minus { prf = prf2 } (n + S m) k and fails. Rightfully, of course, since prf is a proof for a different inequality! And while replace could be used to produce a proof of (S n + m) k (or prf1 would do as well), it does not look like it's possible to simultaneously rewrite and change the proof object so that it matches the rewrite.
How do I work around this? Or, more generally, how do I prove this lemma?
Ok, I guess I solved it. Bottom line: if you don't know what to do, do a lemma!
So we have a proof of two minuends n1, n2 being equal, and we need to produce a proof of n1 `minus` m = n2 `minus` m. Let's write this down!
minusReflLeft : { n1, n2, m : Nat } -> (prf : n1 = n2) -> (prf_n1 : m `LTE` n1) -> (prf_n2 : m `LTE` n2) -> n1 `minus` m = n2 `minus` m
minusReflLeft Refl LTEZero LTEZero = Refl
minusReflLeft Refl (LTESucc prev1) (LTESucc prev2) = minusReflLeft Refl prev1 prev2
I don't even need plusTossS anymore, which can be replaced by a more directly applicable lemma:
plusRightS : (n, m : Nat) -> n + S m = S (n + m)
plusRightS Z m = Refl
plusRightS (S n) m = cong $ plusRightS n m
After that, the original one becomes trivial:
minusPlusTossS : (n, m, k : Nat) ->
{ auto prf1 : k `LTE` n + S m } ->
{ auto prf2 : k `LTE` S n + m } ->
minus (n + S m) k = minus (S n + m) k
minusPlusTossS {prf1} {prf2} n m k = minusReflLeft (plusRightS n m) prf1 prf2
I was trying to write a proof in Idris regarding the following subtraction-based mod operator:
mod : (x, y : Nat) -> Not (y = Z) -> Nat
mod x Z p = void (p Refl)
mod x (S k) _ = if lt x (S k) then x else helper x (minus x (S k)) (S k)
where total
helper : Nat -> Nat -> Nat -> Nat
helper Z x y = x
helper (S k) x y = if lt x y then x else helper k (minus x y) y
The theorem I wanted to prove is that the remainder as produced by "mod" above is always smaller than the divider. Namely,
mod_prop : (x, y : Nat) -> (p : Not (y=0))-> LT (mod x y p) y
I constructed a proof but was stuck by a final hole. My full code is pasted below
mod : (x, y : Nat) -> Not (y = Z) -> Nat
mod x Z p = void (p Refl)
mod x (S k) _ = if lt x (S k) then x else helper x (minus x (S k)) (S k)
where total
helper : Nat -> Nat -> Nat -> Nat
helper Z x y = x
helper (S k) x y = if lt x y then x else helper k (minus x y) y
lteZK : LTE Z k
lteZK {k = Z} = LTEZero
lteZK {k = (S k)} = let ih = lteZK {k=k} in
lteSuccRight {n=Z} {m=k} ih
lte2LTE_True : True = lte a b -> LTE a b
lte2LTE_True {a = Z} prf = lteZK
lte2LTE_True {a = (S _)} {b = Z} Refl impossible
lte2LTE_True {a = (S k)} {b = (S j)} prf =
let ih = lte2LTE_True {a=k} {b=j} prf in LTESucc ih
lte2LTE_False : False = lte a b -> GT a b
lte2LTE_False {a = Z} Refl impossible
lte2LTE_False {a = (S k)} {b = Z} prf = LTESucc lteZK
lte2LTE_False {a = (S k)} {b = (S j)} prf =
let ih = lte2LTE_False {a=k} {b=j} prf in (LTESucc ih)
total
mod_prop : (x, y : Nat) -> (p : Not (y=0))-> LT (mod x y p) y
mod_prop x Z p = void (p Refl)
mod_prop x (S k) p with (lte x k) proof lxk
mod_prop x (S k) p | True = LTESucc (lte2LTE_True lxk)
mod_prop Z (S k) p | False = LTESucc lteZK
mod_prop (S x) (S k) p | False with (lte (minus x k) k) proof lxk'
mod_prop (S x) (S k) p | False | True = LTESucc (lte2LTE_True lxk')
mod_prop (S x) (S Z) p | False | False = LTESucc ?hole
Once I run the type checker, the hole is described as follows:
- + Main.hole [P]
`-- x : Nat
p : (1 = 0) -> Void
lxk : False = lte (S x) 0
lxk' : False = lte (minus x 0) 0
--------------------------------------------------------------------------
Main.hole : LTE (Main.mod, helper (S x) 0 p x (minus (minus x 0) 1) 1) 0
I am not familiar with the syntax of Main.mod, helper (S x) 0 p x (minus (minus x 0) 1) 1 given in the idris-holes window. I guess (S x) 0 p are the three parameters of "mod" while (minus (minus x 0) 1) 1 are the three parameters of the local "helper" function of "mod"?
It seems that it's time to leverage an induction hypothesis. But how can I finish up the proof using induction?
(Main.mod, helper (S x) 0 p x (minus (minus x 0) 1) 1)
can be read as
Main.mod, helper - a qualified name for helper function, which is defined in the where clause of the mod function (Main is a module name);
Arguments of mod which are also passed to helper - (S x), 0 and p (see docs):
Any names which are visible in the outer scope are also visible in the
where clause (unless they have been redefined, such as xs here). A
name which appears only in the type will be in scope in the where
clause if it is a parameter to one of the types, i.e. it is fixed
across the entire structure.
Arguments of helper itself - x, (minus (minus x 0) 1) and 1.
Also below is another implementation of mod which uses Fin n type for the remainder in division by n. It turns out to be easier to reason about, since any value of Fin n is always less than n:
import Data.Fin
%default total
mod' : (x, y : Nat) -> {auto ok: GT y Z} -> Fin y
mod' Z (S _) = FZ
mod' (S x) (S y) with (strengthen $ mod' x (S y))
| Left _ = FZ
| Right rem = FS rem
mod : (x, y : Nat) -> {auto ok: GT y Z} -> Nat
mod x y = finToNat $ mod' x y
finLessThanBound : (f : Fin n) -> LT (finToNat f) n
finLessThanBound FZ = LTESucc LTEZero
finLessThanBound (FS f) = LTESucc (finLessThanBound f)
mod_prop : (x, y : Nat) -> {auto ok: GT y Z} -> LT (mod x y) y
mod_prop x y = finLessThanBound (mod' x y)
Here for convenience I used auto implicits for proofs that y > 0.
In Idris, if I want to remove an element based on predicate, there is filter, dropWhile, takeWhile. However, all these functions return a dependent pair (n : Nat ** Vect n elem).
Is there any function that return back as a Vect type?
For what I could think of:
Convert a dependent pair to Vect
Implement a type that indicate the length vector after transformation (thought I have no idea how), like Here, There
For above ideas, it seems quite cumbersome for 1 (convert every result) or 2 (design each of the type to indicate the result vector length).
Are there any better ways to achieve such behaviour?
dropElem : String -> Vect n String -> Vect ?resultLen String
Maybe this is what you are searching for?
import Data.Vect
count: (ty -> Bool) -> Vect n ty -> Nat
count f [] = 0
count f (x::xs) with (f x)
| False = count f xs
| True = 1 + count f xs
%hint
countLemma: {v: Vect n ty} -> count f v `LTE` n
countLemma {v=[]} = LTEZero
countLemma {v=x::xs} {f} with (f x)
| False = lteSuccRight countLemma
| True = LTESucc countLemma
filter: (f: ty -> Bool) -> (v: Vect n ty) -> Vect (count f v) ty
filter f [] = []
filter f (x::xs) with (f x)
| False = filter f xs
| True = x::filter f xs
Then you con do this:
dropElem: (s: String) -> (v: Vect n String) -> Vect (count ((/=) s) v) String
dropElem s = filter ((/=) s)
You can even reuse the existing filter implementation:
count: (ty -> Bool) -> Vect n ty -> Nat
count f v = fst $ filter f v
filter: (f: ty -> Bool) -> (v: Vect n ty) -> Vect (count f v) ty
filter f v = snd $ filter f v
Why won't the following typecheck:
minusReduces : (n : Nat) -> n `minus` Z = n
minusReduces n = Refl
Yet this will typecheck fine:
plusReduces : (n : Nat) -> Z `plus` n = n
plusReduces n = Refl
minus n doesn't reduce because minus is defined with pattern matching on the first argument:
total minus : Nat -> Nat -> Nat
minus Z right = Z
minus left Z = left
minus (S left) (S right) = minus left right
So you'll need to split your Z and S n cases as well:
minusReduces : (n : Nat) -> n `minus` Z = n
minusReduces Z = Refl
minusReduces (S k) = Refl