I somewhat recently asked a question and resolved the issue with a some applications of the rewrite tactic. I then decided to look back at one of my other questions on code review asking for a review of my attempt to formalize Hilbert's (based on Euclid's) geometry.
From the first question, I learned there is a distinction between propositional equality and boolean equality and propositional equality. Looking back at the some of the axioms I wrote for the Hilbert plane, I utilized boolean equality extensively. Although I am not 100% sure, in light of the answer I received, I suspect that I don't want to use boolean equality.
For instance, take this axiom:
-- There exists 3 non-colinear points.
three_non_colinear_pts : (a : point ** b : point ** c : point **
(colinear a b c = False,
(a /= b) = True,
(b /= c) = True,
(a /= c) = True))
I tried rewriting it to not involve the = True:
-- There exists 3 non-colinear points.
three_non_colinear_pts : (a : point ** b : point ** c : point **
(colinear a b c = False,
(a /= b),
(b /= c),
(a /= c)))
All in all I took the code from my question on codereview removed the == and removed = True:
interface Plane line point where
-- Abstract notion for saying three points lie on the same line.
colinear : point -> point -> point -> Bool
coplanar : point -> point -> point -> Bool
contains : line -> point -> Bool
-- Intersection between two lines
intersects_at : line -> line -> point -> Bool
-- If two lines l and m contain a point a, they intersect at that point.
intersection_criterion : (l : line) ->
(m : line) ->
(a : point) ->
(contains l a = True) ->
(contains m a = True) ->
(intersects_at l m a = True)
-- If l and m intersect at a point a, then they both contain a.
intersection_result : (l : line) ->
(m : line) ->
(a : point) ->
(intersects_at l m a = True) ->
(contains l a = True, contains m a = True)
-- For any two distinct points there is a line that contains them.
line_contains_two_points : (a :point) ->
(b : point) ->
(a /= b) ->
(l : line ** (contains l a = True, contains l b = True ))
-- If two points are contained by l and m then l = m
two_pts_define_line : (l : line) ->
(m : line) ->
(a : point) ->
(b : point) ->
(a /= b) ->
contains l a = True ->
contains l b = True ->
contains m a = True ->
contains m b = True ->
(l = m)
same_line_same_pts : (l : line) ->
(m : line) ->
(a : point) ->
(b : point) ->
(l /= m) ->
contains l a = True ->
contains l b = True ->
contains m a = True ->
contains m b = True ->
(a = b)
-- There exists 3 non-colinear points.
three_non_colinear_pts : (a : point ** b : point ** c : point **
(colinear a b c = False,
(a /= b),
(b /= c),
(a /= c)))
-- Any line contains at least two points.
contain_two_pts : (l : line) ->
(a : point ** b : point **
(contains l a = True, contains l b = True))
-- If two lines intersect at a point and they are not identical, that is the o-
-- nly point they intersect at.
intersect_at_most_one_point : Plane line point =>
(l : line) -> (m : line) -> (a : point) -> (b : point) ->
(l /= m) ->
(intersects_at l m a = True) ->
(intersects_at l m b = True) ->
(a = b)
intersect_at_most_one_point l m a b l_not_m int_at_a int_at_b =
same_line_same_pts
l
m
a
b
l_not_m
(fst (intersection_result l m a int_at_a))
(fst (intersection_result l m b int_at_b))
(snd (intersection_result l m a int_at_a))
(snd (intersection_result l m b int_at_b))
This gives the error:
|
1 | interface Plane line point where
| ~~~~~~~~~~~~~~~~
When checking type of Main.line_contains_two_points:
Type mismatch between
Bool (Type of _ /= _)
and
Type (Expected type)
/home/dair/scratch/hilbert.idr:68:29:
|
68 | intersect_at_most_one_point : Plane line point =>
| ^
When checking type of Main.intersect_at_most_one_point:
No such variable Plane
So, it seems that /= works only for boolean. I have been unable to find a "propositional" /= like:
data (/=) : a -> b -> Type where
Does a propositional not equals exist? Or am I wrong about wanting to change from boolean to propositional equality?
The propositional equivalent to the boolean a /= b would be a = b -> Void. Void is a type with no constructors. So whenever you have a contra : Void, something has gone wrong. So a = b -> Void is to understand as: if you have an a = b, there is a contradiction. Usually written as Not (a = b), which is just a shorthand (Not a = a -> Void).
You're right to change to propositional equality. You might even change your boolean properties like contains : line -> point -> Bool to Contains : line -> point -> Type. Subsequently contains l p = True to Contains l p, and contains l p = False to Not (Contains l p).
That's a case of boolean blindness, i.e. with prf : contains l p = True, the only thing we know is that contains l p is True (and the compiler would need to take a look at contains to guess why it is True). On the other hand, with prf : Contains l p you have a constructed proof prf why the proposition Contains l p holds.
Related
I want to find the type of the lambda expression \x y -> x y y. I proceed as follows.
We go in the reverse order of operations and "unpack" the expression. Assume the whole expression has type A. Then let y have type x1 and \x y -> x y have type B. Then A = B -> x1
We already know the type of y, and let \x y -> x be of the type C. Then B = C -> x1.
The type of \x y -> x is obviously x1->x2->x1. This is given to us in the previous exercise and makes sense because this expression takes in two arguments and returns the first.
Putting it all together we have that:
A = B -> x1 = C -> x1 -> x1 = (x1 -> x2 -> x1) -> x1 -> x1
The correct answer is somehow:
(x1 -> x1 -> x2) -> x1 -> x2
What am I doing wrong?
Here I just write stuff's types under it and go from there:
foo = \x y -> x y y
foo x y = x y y
a b : c
a b b : c
a b : b -> c
a : b -> b -> c
foo : a -> b -> c
~ (b -> b -> c) -> b -> c
And another one:
bar = \x y -> x (y x)
bar x y = x (y x)
a b : c
a (b a) : c
---------
b a : d
b : a -> d
a : d -> c
bar : a -> b -> c
~ (d -> c) -> ( a -> d) -> c
~ (d -> c) -> ((d -> c) -> d) -> c
But,
baz x = x x
a a a : b
a : a -> b
baz : a -> b
~ (a -> b) -> b
~ ((a -> b) -> b) -> b
~ (((a -> b) -> b) -> b) -> b
........
is an "infinite" type, i.e. the process of type derivation never stops.
I have been trying to implement the Incidence Axioms in geometry for Hilbert plane. And came up with the following axioms:
interface (Eq point) => Plane line point where
-- Abstract notion for saying three points lie on the same line.
colinear : point -> point -> point -> Bool
coplanar : point -> point -> point -> Bool
contains : line -> point -> Bool
-- Intersection between two lines
intersects_at : line -> line -> point -> Bool
intersection_def : (contains l a = True) -> (contains m a = True) -> (intersects_at l m a = True)
-- For any two distinct points there is a line that contains them.
line_contains_two_points : (a,b : point) -> (a /= b) = True -> (l : line ** (contains l a = True, contains l b = True ))
-- If two points are contained by l and m then l = m
two_pts_define_line : contains l a = True -> contains l b = True -> contains m a = True -> contains m b = True -> l = m
-- There exists 3 non-colinear points.
three_non_colinear_pts : (a : point ** b : point ** c : point ** (colinear a b c = False, (a /= b) = True, (b /= c) = True, (a /= c) = True))
-- Any lines contains at least two points.
contain_two_pts : (l : line) -> (a : point ** b : point ** (contains l a = True, contains l b = True))
I want to show that a line intersects with another line at most once. So I came up with the following statement:
intersect_at_most_one_point : (l, m : line) -> (a : point) -> (intersects_at l m a = True) -> (intersects_at l m b = True) -> a = b
Which reads:
Given two lines, if they intersect at two points a and b then it must be that a = b.
However I get the error:
When checking type of Main.intersect_at_most_one_point:
When checking argument x to type constructor =:
Can't find implementation for Plane line point
So what I suspect this means is that it wants some sort of data value that I can show satisfies the idea of an incidence geometry. I interpret this mathematically as I need a model for the system. The problem is there are a lot of of "geometries" which satisfy these axioms that are vastly different.
Is it possible to derive theorems about an interface without the need for any any explicit data to work with?
You need to add the Plane constraint to your type signature of intersect_at_most_one_point:
intersect_at_most_one_point : Plane line point =>
(l, m : line) -> (a : point) ->
(intersects_at l m a = True) -> (intersects_at l m b = True) ->
a = b
I am trying to reconstruct the axioms for Hilbert's geometry in Idris. I came up with the following code to represent his axioms:
interface (Eq point) => Plane line point where
colinear : point -> point -> point -> Bool
contains : line -> point -> point -> Bool
axiom1a : (a,b : point) -> (a /= b) = True -> (l : line ** (contains l a b = True))
axiom1b : contains l a b = True -> contains m a b = True -> l = m
axiom2 : (a : point ** (colinear a b c = False) && (a /= b) = True && (b /= c) = True && (a /= c) = True)
axiom3 : (l : line) -> (a ** (contains l a b = True))
axiom2 should read "There exists 3 distinct non-colinear points." I get the following error:
When checking type of Main.axiom2:
When checking argument P to type constructor Builtins.DPair:
Type mismatch between
Bool (Type of _ && _)
and
Type (Expected type)
If I remove && (a /= b) = True && (b /= c) = True && (a /= c) = True the code runs but then the qualification of "distinct" in the axiom is lost. Any help would be appreciated.
colinear a b c = False is a Type, not a Bool. Likewise for (a /= b) = True, (b /= c) = True, and (a /= c) = True.
Instead of using &&, you must use a "type-level &&", i.e. a tuple.
axiom2 : (a : point ** (colinear a b c = False, (a /= b) = True, (b /= c) = True, (a /= c) = True))
However, that would read "There exists a point a that is not colinear with any two other distinct points", which is not always the case. You could instead write this:
axiom2 : (a : point ** (b : point ** (c : point ** (colinear a b c = False, (a /= b) = True, (b /= c) = True, (a /= c) = True))))
That would read "There exist three distinct points that are not colinear". I'm not sure if it's possible to make that any nicer to look at, like (a, b, c : point ** ....
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. :-)
There are times that we want to find an element in a list with a function a -> Bool and replace it using a function a -> a, this may result in a new list:
findr :: (a -> Bool) -> (a -> a) -> [a] -> Maybe [a]
findr _ _ [] = Nothing
findr p f (x:xs)
| p x = Just (f x : xs)
| otherwise = case findr p f xs of Just xs -> Just (x:xs)
_ -> Nothing
Is there any function in the main modules which is similar to this?
Edit: #gallais points out below that you end up only changing the first instance; I thought you were changing every instance.
This is done with break :: (a -> Bool) -> [a] -> ([a], [a]) which gives you the longest prefix which does not satisfy the predicate, followed by the rest of the list.
findr p f list = case break p list of
(xs, y : ys) -> Just (xs ++ f y : ys)
(_, []) -> Nothing
This function is, of course, map, as long as you can combine your predicate function and replacement function the right way.
findr check_f replace_f xs = map (replace_if_needed check_f replace_f) xs
replace_if_needed :: (a -> Bool) -> (a -> a) -> (a -> a)
replace_if_needed check_f replace_f = \x -> if check_f x then replace_f x else x
Now you can do things like findr isAplha toUpper "a123-bc".