I am at an odd place trying to prove an equation:
1 subgoals
A : Type
s : set A
x : A
s0 : s x
x0 : A
s1 : s x0
H : x0 = x
______________________________________(1/1)
stv s x0 s1 = stv s x s0
What I want to do is to use H to substitute x for x0 everywhere. The rest of the proof would be easy, because I have:
Definition set (A : Type) := A -> Prop.
Axiom proof_irrelevance: forall (P:Prop) (p1:P) (p2:P), p1 = p2.
However, rewrite H in * fails, and I cannot rewrite in either s1 or the conclusion separately.
How should I proceed ?
subst x0 did the substitution.
Related
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
I am new to Coq and was wondering what is the difference between the following things:
Class test (f g: nat -> nat) := {
init: f 0 = 0 /\ g 0 = 0;
output: ...another proposition about f and g...;
}.
and
Class test := {
f: nat -> nat;
g: nat -> nat;
init: f 0 = 0 /\ g 0 = 0;
output: ...another proposition about f and g...;
}.
Could anyone provide an explanation ?
The difference between them is referred to as bundling.
Class test (f g: nat -> nat) := {
init: f 0 = 0 /\ g 0 = 0;
output: ...another proposition about f and g...;
}.
is unbundled, and
Class test := {
f: nat -> nat;
g: nat -> nat;
init: f 0 = 0 /\ g 0 = 0;
output: ...another proposition about f and g...;
}.
is bundled.
The advantage of bundling is that you don't need to always provide f and g. The advantage of unbundling is that you can have different instances of the same class sharing the same f and g. If you bundle them, Coq will not be easily convinced that different instances share parameters.
You can read more about this in Type Classes for Mathematics in Type Theory.
To complement Ana’s excellent answer, here is a practical difference:
the unbundled version (call it utest) allows you to write the logical statement utest f g about a specific pair of functions f and g,
whereas the bundled version (call it btest) allows you to state that there exists a pair of functions which satisfies the properties; you can later refer to these functions by the projection names f and g.
So, roughly speaking:
btest is “equivalent” to ∃ f g, utest f g;
utest f' g' is “equivalent” to btest ∧ “the f (resp. g) in the aforementioned proof of btest is equal to f' (resp. g')”.
More formally, here are the equivalences for a minimal example
(in this code, the notation { x : A | B } is a dependent pair type,
i.e. the type of (x, y) where x : A and y : B
and the type B depends on the value x):
(* unbundled: *)
Class utest (x : nat) : Prop := {
uprop : x = 0;
}.
(* bundled: *)
Class btest : Type := {
bx : nat;
bprop : bx = 0;
}.
(* [btest] is equivalent to: *)
Goal { x : nat | utest x } -> btest.
Proof.
intros [x u]. econstructor. exact (#uprop x u).
Qed.
Goal btest -> { x : nat | utest x }.
Proof.
intros b. exists (#bx b). constructor. exact (#bprop b).
Qed.
(* [utest x] is equivalent to: *)
Goal forall x, { b : btest | #bx b = x } -> utest x.
Proof.
intros x [b <-]. constructor. exact (#bprop b).
Qed.
Goal forall x, utest x -> { b : btest | #bx b = x }.
Proof.
intros x u. exists {| bx := x ; bprop := #uprop x u |}. reflexivity.
Qed.
(* NOTE: Here I’ve explicited all implicit arguments; in fact, you
can let Coq infer them, and write just [bx], [bprop], [uprop]
instead of [#bx b], [#bprop b], [#uprop x u]. *)
In this example, we can also observe a difference with respect to computational relevance: utest can live in Prop, because its only member, uprop, is a proposition. On the other hand, I cannot really put btest in Prop, because that would mean that both bx and bprop would live in Prop but bf is computationally relevant. In other words, Coq gives you this warning:
Class btest : Prop := {
bx : nat;
bprop : bx = 0;
}.
(* WARNING:
bx cannot be defined because it is informative and btest is not.
bprop cannot be defined because the projection bx was not defined.
*)
I have some value constraints/properties that I want to check at compile-time. In this example, I want to track whether a vector is normalized or not. I think I have a solution, using type tags, but I need someone with some Elm/FP experience to tell me if I have missed something obvious. Thank you.
module TagExperiment exposing (..)
type Vec t = Vec Float Float Float
type Unit = Unit
type General = General
toGeneral : Vec t -> Vec General
toGeneral (Vec x y z) =
Vec x y z
scaleBy : Vec t -> Vec t -> Vec t
scaleBy (Vec bx by bz) (Vec ax ay az) =
{- Operate on two General's or two Unit's. If you have one of each,
then the Unit needs to be cast to a General.
-}
let
mag =
sqrt ((bx * bx) + (by * by) + (bz * bz))
in
Vec (ax * mag) (ay * mag) (az * mag)
-- These cases work as desired.
a : Vec Unit
a = Vec 0 0 1
b : Vec General
b = Vec 2 2 2
d : Vec Unit
d = scaleBy a a
e : Vec General
e = scaleBy b b
g : Vec General
g = scaleBy (toGeneral a) b
h : Vec General
h = scaleBy b (toGeneral a)
-- Here is where I have trouble.
c : Vec t -- unknown... uh-oh
c = Vec 3 3 3
f : Vec t -- still unknown... sure
f = scaleBy c c
i : Vec Unit -- wrong !!!
i = scaleBy a c
j : Vec Unit -- wrong !!!
j = scaleBy c a
k : Vec General -- lucky
k = scaleBy b c
l : Vec General -- lucky
l = scaleBy c b
{- The trouble is that I am not required to specify a tag for c. I guess the
solution is to write "constructors" and make the built-in Vec constructor
opaque?
-}
newUnitVec : Float -> Float -> Float -> (Vec Unit)
newUnitVec x y z =
-- add normalization
Vec x y z
newVec : Float -> Float -> Float -> (Vec General)
newVec x y z =
Vec x y z
Yes, without Dependant Types the most ergonomic way to ensure constraints on values at compile time is to use opaque types along with a "Parse" approach.
Perhaps something like:
module Vec exposing (UnitVector, Vector, vectorToUnit)
type UnitVector
= UnitVector Float Float Float
type Vector
= Vector Float Float Float
vectorToUnit : Vector -> Maybe UnitVector
vectorToUnit (Vector x y z) =
case ( x, y, z ) of
( 0, 0, 0 ) ->
Nothing
_ ->
normalize x y z
Then, with the only ways to get a UnitVector both defined inside this module and known to obey the constraints, then any time you see a UnitVector at compile-time it is correct to assume the constraints are met.
For vectors in particular, it may be worth having a look at ianmackenzie/elm-geometry for comparison?
This is rather complicated. Sorry I could'n make the example simpler. I'm trying to formalize a theory and interfaces A and B represent my axioms. X and Y are some objects in the theory, mkY creates a Y from two Xs and PropA, PropY and PropYY are some statements about these objects:
interface A a where
PropA : a -> Type
interface B where
X : Type
Y : Type
PropY : Y -> Type
mkY : A X => (x, y : X) -> (z : Y ** PropY z)
PropYY : Y -> Type
mkPropY : A X => {x : X} -> {y : Y} -> PropA x -> PropY y -> PropYY y
lemma1 : (B, A X) => (x, y : X) -> PropA x -> (z : Y ** PropYY z)
lemma1 x y prop_a =
let (z ** propZ) = mkY x y in
(z ** mkPropY prop_a propZ)
Unfortunately rather obvious lemma1 does not compile:
When checking right hand side of Example.case block in lemma1 at /home/sven/code/idris/geometry/src/Euclid/Example.idr:17:9-20 with expected type
(z1 : Y ** PropYY z1)
When checking an application of function Example.mkPropY:
Type mismatch between
PropA x1 (Type of prop_a)
and
PropA x (Expected type)
It seems to me Idris refuses to unify requirement A X from the function header with that introduced by mkY function. When I replace mkPropY prop_a propZ with a hole and ask for its type, I get this:
constraint : B
z : Y
propZ : PropY z
x : X
y : X
constraint1 : A X
prop_a : PropA x
constraint2 : A X
--------------------------------------
whatIsIt : PropYY z
Here constraint1 and constraint2 are the same, and yet there's two of them, which seems to be the root cause of the problem. So why does Idris introduce this additional constraint and how do I make it work?
I'm not sure why Idris thinks with mkY there could be another constraint in play (as by the looks of it nothing in the hole is constrained by it, even with :set showimplicits). Maybe someone else can explain why, but for now it usually helps to make constraints explicit:
lemma1 : (B, a_const : A X) => (x, y : X) -> PropA x -> (z : Y ** PropYY z)
lemma1 #{a_const} x y prop_a =
let (z ** propZ) = mkY #{a_const} x y in
(z ** mkPropY prop_a propZ)
(Maybe rewriting B as interface B x y where; … would help, so the scope over X and Y is clearer, but I didn't try this.)
I'm working through the Idris book, and I'm doing the first exercises on proof.
With the exercise to prove same_lists, I'm able to implement it like this, as matching Refl forces x and y to unify:
total same_lists : {xs : List a} -> {ys : List a} ->
x = y -> xs = ys -> x :: xs = y :: ys
same_lists Refl Refl = Refl
However, when I try to prove something else in the same manner, I get mismatches. For example:
total allSame2 : (x, y : Nat) -> x = y -> S x = S y
allSame2 x y Refl = Refl
The compiler says:
Type mismatch between
y = y (Type of Refl)
and
x = y (Expected type)
If I case-match after the =, either explicitly or with a lambda, it works as expected:
total allSame2 : (x : Nat) -> (y : Nat) -> x = y -> S x = S y
allSame2 x y = \Refl => Refl
What's the difference here?
Another modification that works is making the problematic arguments implicit:
total allSame2 : {x : Nat} -> {y : Nat} -> x = y -> S x = S y
allSame2 Refl = Refl
I do not know all the details, but I can give you a rough idea. In Idris, the parameter lists of named functions are special in that it is part of dependent pattern matching. When you pattern match it also rewrites the other parameters.
same_lists x y Refl = Refl is not valid, I roughly guess, because Idris is rewriting x and y to be the same, and you are not allowed to then give different names to this single value — I hope someone can give a better explanation of this mechanism. Instead you may use same_lists x x Refl = Refl — and note that the name x is not important, just that the same name is used in both sites.
A lambda parameter is apart from the named parameter list. Therefore, since you are doing the matching in the lambda, Idris is only going to rewrite the other parameters at that point. The key is that with the first example Idris wants to do it all at once because it is part of the same parameter list.
With the final example the only change is that you did not give distinct names to the parameters. It would have also been valid to use all_same _ _ Refl = Refl. When the parameters are implicit, Idris will fill them in correctly for you.
Finally you can consider same_lists = \x, y, Refl => Refl which also works. This is because Idris does not rewrite in unnamed parameter lists (i.e. lambda parameters).