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.
Related
The following snippet compiles fine in Idris 2,
divMod2 : Nat -> (Nat, Bool)
divMod2 Z = (Z, False)
divMod2 (S Z) = (Z, True)
divMod2 (S (S n)) = case divMod2 n of (k, r) => (S k, r)
lm : {q:_} -> divMod2 (q+q) = (q, False)
lm {q=0} = Refl
lm {q=S q} = rewrite sym $ plusSuccRightSucc q q in
rewrite lm {q} in Refl
but it caused Idris 1.3.4 to fail:
|
14 | rewrite lm {q} in Refl
| ~~~~~~
When checking right hand side of lm with expected type
divMod2 (S q + S q) = (S q, False)
When checking an application of function rewrite__impl:
Type mismatch between
(q1, False) = (q1, False) (Type of Refl)
and
case divMod2 (plus q q) of (k, r) => (S k, r) = (q1, False) (Expected type)
Specifically:
Type mismatch between
(q1, False)
and
case divMod2 (plus q q) of
(k, r) => (S k, r)
I know Idris 1 has become obsolete, but am still curious about the underlying reason behind this observation. Could someone help to explain this?
I've done a bit more dig here and found surprisingly that Idris1 failed to compile the following even simpler code snippet, which compiles correctly by Idris2:
f : Nat -> (Nat, Bool)
f Z = (Z, False)
f (S n) = case f n of (k, r) => (S k, r)
h2 : {q:_} -> f (S (q+1)) = case f (q+1) of (k, r) => (S k, r)
h2 = Refl
It spit error message:
|
19 | h2 = Refl
| ~~~~
When checking right hand side of h2 with expected type
f (S (q + 1)) = case f (q + 1) of (k, r) => (S k, r)
Type mismatch between
case f (plus q 1) of (k, r) => (S k, r) = case f (plus q 1) of (k, r) => (S k, r) (Type of Refl)
and
case f (plus q 1) of (k, r) => (S k, r) = case f (plus q 1) of (k, r) => (S k, r) (Expected type)
Specifically:
Type mismatch between
case f (plus q 1) of
(k, r) => (S k, r)
and
case f (plus q 1) of
(k, r) => (S k, r)
Aren't the two mismatching terms identical? Why couldn't be constructed with Refl?
This compiles:
data ThreeEq : a -> b -> c -> Type where
Same3 : (x : a) -> ThreeEq x x x
allSameS : (x, y, z : Nat) -> ThreeEq x y z -> ThreeEq (S x) (S y) (S z)
allSameS k k k (Same3 k) = Same3 (S k)
But with one small change to Same3, it no longer compiles. Can anyone explain why?
data ThreeEq : a -> b -> c -> Type where
Same3 : x -> ThreeEq x x x
allSameS : (x, y, z : Nat) -> ThreeEq x y z -> ThreeEq (S x) (S y) (S z)
allSameS k k k (Same3 k) = Same3 (S k)
Here's the error message:
- + Errors (1)
`-- Amy2.idr line 5 col 0:
When checking left hand side of allSameS:
When checking an application of Main.allSameS:
Type mismatch between
ThreeEq x x x (Type of Same3 _)
and
ThreeEq k y z (Expected type)
Specifically:
Type mismatch between
Type
and
Nat
Here is the difference
data ThreeEq : a -> b -> c -> Type where
Same3 : (x : a) -> ThreeEq x x x
^ ^
| |
| Type
Value
Here, Same3 Z builds a value of type Three Z Z Z.
data ThreeEq : a -> b -> c -> Type where
Same3 : x -> ThreeEq x x x
^
|
Type
And now, Same3 Z builds a value of type Three Nat Nat Nat.
Testing out an "easy" example of identity types, mod equality, but transitivity proof wont type check, even from the template. More than a fix, I want to know why?
Here's a snippet of the minimal problem
data ModEq : (n:Nat) -> (x:Nat) -> (y:Nat) -> Type where
{- 3 constructors
reflexive: x == x (mod n),
left-induct: x == y (mod n) => (x+n) == y (mod n)
right-induct: x == y (mod n) => x == (y+n) (mod n)
-}
Reflex : (x:Nat) -> ModEq n x x --- Needs syntatic sugar, for now prefix
LInd : (ModEq n x y) -> ModEq n (x+n) y
RInd : (ModEq n x y) -> ModEq n x (y+n)
{----- Proof of transitive property. -----}
total
isTrans : (ModEq n x y) -> (ModEq n y z) -> (ModEq n x z)
{- x=x & x=y => x=y -}
isTrans (Reflex x) v = v
isTrans u (Reflex y) = u
{- ((x=y=>(x+n)=y) & y=z) => x=y & y=z => x=z (induct) => (x+n)=z -}
isTrans (LInd u) v = LInd (isTrans u v)
isTrans u (RInd v) = RInd (isTrans u v)
{- (x=y=>x=(y+n)) & (y=z=>(y+n)=z) => x=y & y=z => x=z (induct) -}
isTrans (RInd u) (LInd v) = isTrans u v
The type mismatch is in the last line, even though from the comment line I really cannot tell why logically its wrong. Here's the error:
48 | isTrans (RInd u) (LInd v) = isTrans u v
| ~~~~~~~
When checking left hand side of isTrans:
When checking an application of Main.isTrans:
Type mismatch between
ModEq n (x + n) z (Type of LInd v)
and
ModEq n (y + n) z (Expected type)
Specifically:
Type mismatch between
plus x n
and
plus y n
Not only am I confused by how LInd v got assigned the (wrong seeming) type ModEq n (x+n) z, but I point out that when I simply try the "type-define-refine" approach with the built in template I get:
isTrans : (ModEq n x y) -> (ModEq n y z) -> (ModEq n x z)
isTrans (RInd _) (LInd _) = ?isTrans_rhs_1
And even this wont type-check, it complains:
40 | isTrans (RInd _) (LInd _) = ?isTrans_rhs_1
| ~~~~~~~
When checking left hand side of isTrans:
When checking an application of Main.isTrans:
Type mismatch between
ModEq n (x + n) z (Type of LInd _)
and
ModEq n (y + n) z (Expected type)
Specifically:
Type mismatch between
plus x n
and
plus y n
The issue is that the compiler isn't able to deduce in your last case that y = {x + n}. You can give it this hint, though:
isTrans : (ModEq n x y) -> (ModEq n y z) -> (ModEq n x z)
isTrans (Reflex _) v = v
isTrans u (Reflex _) = u
isTrans (LInd u) v = LInd $ isTrans u v
isTrans u (RInd v) = RInd $ isTrans u v
isTrans (RInd u) (LInd v) {n} {x} {y = x + n} = ?isTrans_rhs
Which gives you the following goal for isTrans_rhs:
x : Nat
n : Nat
u : ModEq n x x
z : Nat
v : ModEq n x z
--------------------------------------
isTrans_rhs : ModEq n x z
and thus, you can conclude with isTrans (RInd u) (LInd v) {n} {x} {y = x + n} = v
In (*1) one can read next
rewrite prf in expr
If we have prf : x = y, and the required type for expr is some property of x, the rewrite ... in syntax will search for x in the required type of expr and replace it with y.
Now, I have next piece of code (you can copy it to editor and try ctrl-l)
module Test
plusCommZ : y = plus y 0
plusCommZ {y = Z} = Refl
plusCommZ {y = (S k)} = cong $ plusCommZ {y = k}
plusCommS : S (plus y k) = plus y (S k)
plusCommS {y = Z} = Refl
plusCommS {y = (S j)} {k} = let ih = plusCommS {y=j} {k=k} in cong ih
plusComm : (x, y : Nat) -> plus x y = plus y x
plusComm Z y = plusCommZ
plusComm (S k) y =
let
ih = plusComm k y
prfXeqY = sym ih
expr = plusCommS {k=k} {y=y}
-- res = rewrite prfXeqY in expr
in ?hole
below is how hole looks like
- + Test.hole [P]
`-- k : Nat
y : Nat
ih : plus k y = plus y k
prfXeqY : plus y k = plus k y
expr : S (plus y k) = plus y (S k)
-----------------------------------------
Test.hole : S (plus k y) = plus y (S k)
The Question.
It looks to me like expr (from *1) in commented line equals to S (plus y k) = plus y (S k). And prf equals to plus y k = plus k y where x is plus y k and y is plus k y. And rewrite should search for x (namely for plus y k) in expr (namely S (plus y k) = plus y (S k) and should replace x with y (namely with plus k y). And result (res) should be S (plus k y) = plus y (S k).
But this does not work.
I have next answer from idris
rewriting plus y k to plus k y did not change type letty
I could guess rewrite is intended to change type of the resulting expression only. So, it is not working within body of let expression, but only in it's 'in' part. Is this correct?
(*1) http://docs.idris-lang.org/en/latest/proofs/patterns.html
PS. Example from tutorial works fine. I'm just curious to know why the way I've tried to use rewrite didn't work.
Though not stated explicitly stated in the docs, rewrite is syntax-sugary invocation of an Elab tactics script (defined around here).
To why your example does not work: the "required type of expr" isn't found; with just res = rewrite prfXeqY in expr alone, it is unclear, which type res should have (even the unifier could resolve this with let res = … in res.) If you specify the required type, it works as expected:
res = the (S (plus k y) = plus y (S k)) (rewrite prfXeqY in expr)
Unfortunately you did not provide the exact line which makes your code misbehave, somehow you must have done something strange, since with your reasoning you outlined above the code works well:
let
ih = plusComm k y -- plus k y = plus y k
px = plusCommS {k=k} {y=y} -- S (plus y k) = plus y (S k)
in rewrite ih in px
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