How to prove that the boolean inequality of a type with itself is uninhabited in Idris? - dependent-type

I was wondering how to prove that (So (not (y == y))) is an instance of Uninhabited, and I'm not sure how to go about it. Is it provable in Idris, or is not provable due to the possibility of a weird Eq implementation for y?

The Eq interface does not require an implementation to follow the normal laws of equality. But, we can define an extended LawfulEq interface which does:
%default total
is_reflexive : (t -> t -> Bool) -> Type
is_reflexive {t} rel = (x : t) -> rel x x = True
is_symmetric : (t -> t -> Bool) -> Type
is_symmetric {t} rel = (x : t) -> (y : t) -> rel x y = rel y x
is_transitive : (t -> t -> Bool) -> Type
is_transitive {t} rel = (x : t) -> (y : t) -> (z : t) -> rel x y = True -> rel x z = rel y z
interface Eq t => LawfulEq t where
eq_is_reflexive : is_reflexive {t} (==)
eq_is_symmetric : is_symmetric {t} (==)
eq_is_transitive : is_transitive {t} (==)
The result asked for in the question can be proved for type Bool:
so_false_is_void : So False -> Void
so_false_is_void Oh impossible
so_not_y_eq_y_is_void : (y : Bool) -> So (not (y == y)) -> Void
so_not_y_eq_y_is_void False = so_false_is_void
so_not_y_eq_y_is_void True = so_false_is_void
The result can be proved not true for the following Weird type:
data Weird = W
Eq Weird where
W == W = False
weird_so_not_y_eq_y : (y : Weird) -> So (not (y == y))
weird_so_not_y_eq_y W = Oh
The Weird (==) can be shown to be not reflexive, so an implementation of LawfulEq Weird is not possible:
weird_eq_not_reflexive : is_reflexive {t=Weird} (==) -> Void
weird_eq_not_reflexive is_reflexive_eq =
let w_eq_w_is_true = is_reflexive_eq W in
trueNotFalse $ trans (sym w_eq_w_is_true) (the (W == W = False) Refl)

Shersh is right: you can't. Implementations of (==) aren't guaranteed to be reflexive, so it might not be true.
You could restrict the type of y so that you are proving a property of a specific implementation of (==), but I suspect you want to use decEq and (=) instead of So and (==). It's easy to show Not (y = y) is uninhabited.

Related

Applying known proofs in Idris 1 interactive elaborator

I am trying to get some familiarity with theorem proving in Idris1 by exercise and am running into trouble.
Suppose I have the following definition for naturals and the following theorems that I want to prove:
data Natural = Z | S Natural
plus : Natural -> Natural -> Natural
plus x Z = x
plus x (S y) = S (plus x y)
succBoth : {a : Natural} -> {b : Natural} -> (a = b) -> (S a = S b)
succBoth = ?succBothProof
plusZero : (y : Natural) -> plus Z y = y
plusZero = ?plusZeroProof
plusSwitch : (x : Natural) -> (y : Natural) -> plus (S x) y = S (plus x y)
plusSwitch = ?plusSwitchProof
plusComm : (x : Natural) -> (y : Natural) -> plus x y = plus y x
plusComm = ?plusCommProof
I already have written proofs for the first three. Now, when I want to prove the last theorem, I run into necessity of applying an earlier proof.
Idris> :l Peano.idr
Holes: Peano.plusCommProof
*Peano> :elab plusCommProof
-Peano.plusCommProof> intro `{{x}}
...
-Peano.plusCommProof> intro `{{y}}
...
-Peano.plusCommProof> induction (Var `{{y}})
...
-Peano.plusCommProof> compute
...
-Peano.plusCommProof> attack
---------- Other goals: ----------
{Z_103},{S_104}
---------- Assumptions: ----------
x : Natural
y : Natural
---------- Goal: ----------
{hole_7} : x = plus Z x
It would be natural to apply plusZero at this stage, but I run into issues trying to do that. I try to apply it via rewriteWith, keeping in mind that plusZero takes a Natural type argument. I try to supply it with the x variable, thinking that it will be able to infer its Natural type from assumptions, but no luck:
-Peano.plusCommProof> rewriteWith `(plusZero (Var `{{x}}))
(input):1:15-35:When checking argument y to function Peano.plusZero:
Type mismatch between
Raw (Type of Var _)
and
Natural (Expected type)
How does one "cast" the Raw variable into its type in context?
I couldn't get the Idris 1 version to work but I did install Idris 2 and wrote the proofs in its style instead.
module Peano
data Natural = Zero | Succ Natural
plus : Natural -> Natural -> Natural
plus x Zero = x
plus x (Succ y) = Succ (plus x y)
succBoth : {a : Natural} -> {b : Natural} -> (a = b) -> (Succ a = Succ b)
succBoth rfl = cong (\ a => Succ a) rfl
plusZero : (y : Natural) -> plus Zero y = y
plusZero Zero = Refl
plusZero (Succ y)
= let assumption = plusZero y in
rewrite assumption in Refl
plusSwitch : (x : Natural) -> (y : Natural) ->
plus (Succ x) y = Succ (plus x y)
plusSwitch x Zero = Refl
plusSwitch x (Succ y)
= let assumption = plusSwitch x y in
rewrite assumption in Refl
plusComm : (x : Natural) -> (y : Natural) -> plus x y = plus y x
plusComm x Zero = rewrite plusZero x in Refl
plusComm x (Succ y)
= let assumption = plusComm x y in
rewrite plusSwitch y x in
rewrite assumption in Refl
Admittedly much more compact but I prefer the Idris 1 :elab style for readability

Proving theorems about functions with cases

Let's say we have a function merge that, well, just merges two lists:
Order : Type -> Type
Order a = a -> a -> Bool
merge : (f : Order a) -> (xs : List a) -> (ys : List a) -> List a
merge f xs [] = xs
merge f [] ys = ys
merge f (x :: xs) (y :: ys) = case x `f` y of
True => x :: merge f xs (y :: ys)
False => y :: merge f (x :: xs) ys
and we'd like to prove something clever about it, for instance, that merging two non-empty lists produces a non-empty list:
mergePreservesNonEmpty : (f : Order a) ->
(xs : List a) -> (ys : List a) ->
{auto xsok : NonEmpty xs} -> {auto ysok : NonEmpty ys} ->
NonEmpty (merge f xs ys)
mergePreservesNonEmpty f (x :: xs) (y :: ys) = ?wut
Inspecting the type of the hole wut gives us
wut : NonEmpty (case f x y of True => x :: merge f xs (y :: ys) False => y :: merge f (x :: xs) ys)
Makes sense so far! So let's proceed and case-split as this type suggests:
mergePreservesNonEmpty f (x :: xs) (y :: ys) = case x `f` y of
True => ?wut_1
False => ?wut_2
It seems reasonable to hope that the types of wut_1 and wut_2 would match the corresponding branches of merge's case expression (so wut_1 would be something like NonEmpty (x :: merge f xs (y :: ys)), which can be instantly satisfied), but our hopes fail: the types are the same as for the original wut.
Indeed, the only way seems to be to use a with-clause:
mergePreservesNonEmpty f (x :: xs) (y :: ys) with (x `f` y)
mergePreservesNonEmpty f (x :: xs) (y :: ys) | True = ?wut_1
mergePreservesNonEmpty f (x :: xs) (y :: ys) | False = ?wut_2
In this case the types would be as expected, but this leads to repeating the function arguments for every with branch (and things get worse once with gets nested), plus with doesn't seem to play nice with implicit arguments (but that's probably worth a question on its own).
So, why doesn't case help here, are there any reasons besides purely implementation-wise behind not matching its behaviour with that of with, and are there any other ways to write this proof?
The stuff to the left of the | is only necessary if the new information somehow propagates backwards to the arguments.
mergePreservesNonEmpty : (f : Order a) ->
(xs : List a) -> (ys : List a) ->
{auto xsok : NonEmpty xs} -> {auto ysok : NonEmpty ys} ->
NonEmpty (merge f xs ys)
mergePreservesNonEmpty f (x :: xs) (y :: ys) with (x `f` y)
| True = IsNonEmpty
| False = IsNonEmpty
-- for contrast
sym' : (() -> x = y) -> y = x
sym' {x} {y} prf with (prf ())
-- matching against Refl needs x and y to be the same
-- now we need to write out the full form
sym' {x} {y=x} prf | Refl = Refl
As for why this is the case, I do believe it's just the implementation, but someone who knows better may dispute that.
There's an issue about proving things with case: https://github.com/idris-lang/Idris-dev/issues/4001
Because of this, in idris-bi we ultimately had to remove all cases in such functions and define separate top-level helpers that match on the case condition, e.g., like here.

Proving (x : t) -> (x == x) = True in Idris

Given this definition:
data LType : Type where
TNat : LType
TFun : LType -> LType -> LType
Eq LType where
(==) TNat TNat = True
(==) (TFun l0 l1) (TFun r0 r1) = (l0 == r0) && (l1 == r1)
(==) _ _ = False
I am trying to prove the following:
ltype_eq : (t : LType) -> (t == t) = True
However I am getting stuck in an infinite number of proofs:
ltype_eq : (t : LType) -> (t == t) = True
ltype_eq TNat = Refl
ltype_eq (TFun x y) = ?ltype_eq_rhs_2
with the type of ?ltype_eq_rhs_2 being:
x : LType
y : LType
--------------------------------------
ltype_eq_rhs_2 : Main.LType implementation of Prelude.Interfaces.Eq, method == x
x &&
Delay (Main.LType implementation of Prelude.Interfaces.Eq, method == y
y) =
True
So it is basically a recursive proof. Any ideas on how to prove it?
I realised how to prove it right after posting. Here is the proof:
ltype_eq : (t : LType) -> (t == t) = True
ltype_eq TNat = Refl
ltype_eq (TFun x y) =
rewrite ltype_eq x in
rewrite ltype_eq y in
Refl

Problems with equational proofs and interface resolution in Idris

I'm trying to model Agda style equational reasoning proofs for Setoids (types with an equivalence relation). My setup is as follows:
infix 1 :=:
interface Equality a where
(:=:) : a -> a -> Type
interface Equality a => VerifiedEquality a where
eqRefl : {x : a} -> x :=: x
eqSym : {x, y : a} -> x :=: y -> y :=: x
eqTran : {x, y, z : a} -> x :=: y -> y :=: z -> x :=: z
Using such interfaces I could model some equational reasoning combinators like
Syntax.PreorderReasoning from Idris library.
syntax [expr] "QED" = qed expr
syntax [from] "={" [prf] "}=" [to] = step from prf to
namespace EqReasoning
using (a : Type, x : a, y : a, z : a)
qed : VerifiedEquality a => (x : a) -> x :=: x
qed x = eqRefl {x = x}
step : VerifiedEquality a => (x : a) -> x :=: y -> (y :=: z) -> x :=: z
step x prf prf1 = eqTran {x = x} prf prf1
The main difference from Idris library is just the replacement of propositional equality and their related functions to use the ones from VerifiedEquality interface.
So far, so good. But when I try to use such combinators, I run in problems that, I believe, are related to interface resolution. Since the code is part of a matrix library that I'm working on, I posted the relevant part of it in the following gist.
The error occurs in the following proof
zeroMatAddRight : ( VerifiedSemiring s
, VerifiedEquality s ) =>
{r, c : Shape} ->
(m : M s r c) ->
(m :+: (zeroMat r c)) :=: m
zeroMatAddRight {r = r}{c = c} m
= m :+: (zeroMat r c)
={ addMatComm {r = r}{c = c} m (zeroMat r c) }=
(zeroMat r c) :+: m
={ zeroMatAddLeft {r = r}{c = c} m }=
m
QED
that returns the following error message:
When checking right hand side of zeroMatAddRight with expected type
m :+: (zeroMat r c) :=: m
Can't find implementation for Semiring a
Possible cause:
./Data/Matrix/Operations/Addition.idr:112:11-118:1:When checking an application of function Algebra.Equality.EqReasoning.step:
Type mismatch between
m :=: m (Type of qed m)
and
y :=: z (Expected type)
At least to me, it appears that this error is related with interface resolution that isn't interacting well with syntax extensions.
My experience is that such strange errors can be solved by passing implicit parameters explicitly. The problem is that such solution will kill the "readability" of equational reasoning combinator proofs.
Is there a way to solve this? The relevant part for reproducing this error is available in previously linked gist.

Idris: arithmetics for bounded Double

I am new to Idris. I need to create a data describing a bounded number. So I've made such data with such a constructor:
data BoundedDouble : (a, b : Double) -> Type where
MkBoundedDouble : (x : Double) ->
{auto p : a <= x && x <= b = True} ->
BoundedDouble a b
It seems to create a Double between a and b.
And here is a simple example of use:
test : BoundedDouble 0.0 1.0
test = MkBoundedDouble 0.0
It works. But now I want to implement Num interface for BoundedDouble. I tried this:
Num (BoundedDouble a b) where
(MkBoundedDouble x) + (MkBoundedDouble y) =
MkBoundedDouble (ifThenElse (x + y > b)
(x + y - (b - a))
(ifThenElse (x + y < a)
(x + y + (b - a))
(x + y)))
But it doesn't work, I guess why, but I can't explain it.
How should I implement the addition?
I don't know exactly what should I do or read to understand it.
There are two problems here. Double arithmetic is defined with primitive functions. Idris can't even proof that a <= b = True -> b <= c = True -> a <= c = True (which, by the way, does not even hold all the time - so this is not Idris' fault.) There is no proof for a <= b = True other then just checking it, what you tried with the ifThenElse.
When working with such blind run-time proofs (so just … = True), Data.So is quite helpful. ifThenElse (a <= x) … … branches off given a boolean check, but the code in the branches does not know about result of the check. With choose (a <= x) you get the result for the branches, with Left prf and prf : So (a <= x) or Right prf and prf : So (not (a <= x)).
I suppose if the result of adding two bounded doubles would be bigger then the upper bound, the result should be this upper bound. Lets make a first attempt:
import Data.So
data BoundedDouble : (a, b : Double) -> Type where
MkBoundedDouble : (x : Double)
-> {auto high : So (a <= x)}
-> {auto low : So (x <= b)}
-> BoundedDouble a b
Num (BoundedDouble a b) where
(+) (MkBoundedDouble u) (MkBoundedDouble v) =
let x = u + v
in case (choose (a <= x), choose (x <= b)) of
(Left _, Left _) => MkBoundedDouble x
(Right _, _) => ?holeMin
(_, Right _) => ?holeMax
This already typechecks, but has holes in it. We want to set ?holeMin to MkBoundedDouble a and ?holeMax to MkBoundedDouble b. However, MkBoundedDouble right now needs two proofs: high and low. In the case of ?holeMax those would be with x = b So (a <= b) and So (b <= b). Again, Idris does not know that b <= b for every b : Double. So we would need to choose again to get these proofs:
(_, Right _) => case (choose (a <= b), choose (b <= b)) of
(Left _, Left _) => MkBoundedDouble b
_ => ?what
Because Idris cannot see that b <= b, the function would be partial. We could cheat and use for example MkBoundedDouble u in ?what, so the function will typecheck and hope that this will indeed never occur.
There is also the possibility to convince the type checker with force that b <= b is always true:
data BoundedDouble : (a, b : Double) -> Type where
MkBoundedDouble : (x : Double)
-> {auto rightSize : So (a <= b)}
-> {auto high : So (a <= x)}
-> {auto low : So (x <= b)}
-> BoundedDouble a b
DoubleEqIsSym : (x : Double) -> So (x <= x)
DoubleEqIsSym x = believe_me (Oh)
Num (BoundedDouble a b) where
(+) (MkBoundedDouble u) (MkBoundedDouble v) =
let x = u + v
in case (choose (a <= x), choose (x <= b)) of
(Left _, Left _) => MkBoundedDouble x
(Right _, _) => MkBoundedDouble a {high=DoubleEqIsSym a}
(_, Right _) => MkBoundedDouble b {low=DoubleEqIsSym b}
Or we could be even safer and put the proofs for the upper and lower bounds in the data constructor, so we can use them in ?holeMin and ?holeMax. This would be:
import Data.So
data BoundedDouble : (a, b : Double) -> Type where
MkBoundedDouble : (x : Double)
-> {auto rightSize : So (a <= b)}
-> {auto leftId : So (a <= a)}
-> {auto rightId : So (b <= b)}
-> {auto high : So (a <= x)}
-> {auto low : So (x <= b)}
-> BoundedDouble a b
Num (BoundedDouble a b) where
(+) (MkBoundedDouble u) (MkBoundedDouble v) =
let x = u + v
in case (choose (a <= x), choose (x <= b)) of
(Left _, Left _) => MkBoundedDouble x
(Right _, _) => MkBoundedDouble a
(_, Right _) => MkBoundedDouble b
You see that even that the constructor is packed with proofs, they don't complicate the implementation. And they should get erased in the actual run-time code.
However, as an exercise you could try to implement Num for
data BoundedDouble : (a, b : Double) -> Type where
MkBoundedDouble : (x : Double)
-> {auto rightSize : So (a <= b)}
-> {auto high : So (a <= x)}
-> {auto low : So (x <= b)}
-> BoundedDouble a b
Min : {auto rightSize : So (a <= b)} -> BoundedDouble a b
Max : {auto rightSize : So (a <= b)} -> BoundedDouble a b
Sadly, there aren't many resources for Idris yet. Besides the tutorial there is a book in development, that I would recommend. It gives more approachable exercises than working with primitive types. :-)