Hi Kevin
> I'm writing a small article that touches on what dependent types are
> capable of. The problem is that although I have some background in Haskell,
> I'm quite new at this. I'm slowly making my way through some books on Coq
> and Agda, but right now I'm in over my head. :)
It's still a bit rough to get going with these things, unfortunately.
> Here's what I'm working
> with:
https://gist.github.com/KMahoney/b2a7856d179999daa2de
>
> Now, in order to publish my article I don't actually need to provide the
> proofs (although that would be nice for me to understand), but I would like
> to fact check that
>
> 1) My types look plausible for what I'm trying to prove and
> 2) It is in fact possible to prove these properties.
I think that your types are close, but not correct for what you want.
This is because they are mixing two different notions of equality: the
built-in notion of equality in the type system, and the Boolean check
that a user implements as part of implementing Eq. Elem uses the
built-in notion of equality, while Eq can do basically anything.
Here's a counterexample for your nothingProof - a similar one could be
built for the other proof:
module Main
import Data.Vect
import Data.Fin
%default total
find : Eq x => List x -> x -> Maybe Int
find [] elem = Nothing
find (y :: xs) elem = if y == elem then (Just 0) else (map (\ n => n +
1) (find xs elem))
find2 : Eq x => {n : Nat} -> Vect n x -> x -> Maybe (Fin n)
find2 [] elem = Nothing
find2 (y :: xs) elem = if y == elem then (Just FZ) else (map FS (find2
xs elem))
ex : find2 [1, 2, 3] 2 = Just 1
ex = Refl
nothingProof : Eq x => (n : Nat) -> (v : Vect n x) -> (e : x) -> Not
(Elem e v) -> (find2 v e) = Nothing
nothingProof n v elem notIn = ?noIdea1
justProof : Eq x => (n : Nat) -> (v : Vect n x) -> (e : x) -> (find2 v e
= Just r) -> (index r v == e) = True
justProof n v elem findProof = ?noIdea2
data Counterexample : Type where
One : Counterexample
Two : Counterexample
implementation Eq Counterexample where
_ == _ = True
implementation Uninhabited (Just x = Nothing) where
uninhabited Refl impossible
nothingCounterexample : Void
nothingCounterexample =
absurd $ nothingProof _ [One] Two notIn
where notIn : Elem Two [One] -> Void
notIn Here impossible
notIn (There Here) impossible
What you probably should do, based on your stated goal, is to use the
DecEq interface instead of the Eq interface. This one is set up such
that its notion of equality _must_ correspond with the type theory's
notion of equality. Additionally, instead of returning Fin elements, try
returning an Elem proof directly - that has just as much information
about the position of the element in the Vect, because each Here or
There constructor corresponds to either an FS or an FZ constructor, but
it also guarantees the placement.
Here's an example of how it could work to find the Elem directly. I also
wrote a Maybe (Fin n) version that uses DecEq, and then proved the
lemmas that you wanted for your version. I put them in one proof instead
of two to save time and space - the type does a case analysis to figure
out which version it's working on.
I didn't use anything not in the Idris tutorial as far as I can see,
with the possible exception of the Uninhabited interface. The way that
works is this: a proof that something is impossible can go in an
Uninhabited implementation, and then "absurd" resolves that
implementation at a use site so we don't have to remember custom names
for all those lemmas.
Here's how I might do what you're going for, with a few explanatory
comments:
module Main
import Data.Vect
import Data.Fin
%default total
||| If a value is neither at the head of a Vect nor in the tail of
||| that Vect, then it is not in the Vect.
notHeadNotTail : Not (x = y) -> Not (Elem x xs) -> Not (Elem x (y :: xs))
notHeadNotTail notHead notTail Here = notHead Refl
notHeadNotTail notHead notTail (There tl) = notTail tl
||| Determine the first location of an element in a Vect. The types
||| guarantee that we find a valid index, but not that it's the first
||| occurrence.
find : DecEq a => (x : a) -> (xs : Vect n a) -> Dec (Elem x xs)
find x [] = No absurd
find x (y :: xs) with (decEq x y)
find x (x :: xs) | Yes Refl = Yes Here
find x (y :: xs) | No notHead with (find x xs)
find x (y :: xs) | No notHead | Yes prf = Yes (There prf)
find x (y :: xs) | No notHead | No notTail =
No $ notHeadNotTail notHead notTail
||| Find a Fin giving a position in a Vect, instead of an Elem
findIndex : DecEq a => (x : a) -> (xs : Vect n a) -> Maybe (Fin n)
findIndex x [] = Nothing
findIndex x (y :: xs) =
case decEq x y of
Yes _ => pure FZ
-- the following could be written as:
-- No _ => do tailIdx <- findIndex x xs
-- pure (FS tailIdx)
-- but idiom brackets are a bit cleaner:
No _ => [| FS (findIndex x xs) |]
-- (that was like the Haskell FS <$> findIndex x xs)
-- Prove that the Fin version corresponds to the Elem
-- version. Correspondence here means that if a Fin is found, we can
-- construct an Elem proof, and if a Fin is not found, then it is
-- provably impossible to construct an Elem proof.
findIndexOk : DecEq a => (x : a) -> (xs : Vect n a) ->
case findIndex x xs of
Just i => Elem x xs
Nothing => Not (Elem x xs)
findIndexOk x [] = absurd -- this is proved in the library already! :-)
findIndexOk x (y :: xs) with (decEq x y)
findIndexOk x (x :: xs) | Yes Refl = Here
findIndexOk x (y :: xs) | No notHead with (findIndexOk x xs) -- get an
induction hypothesis
findIndexOk x (y :: xs) | No notHead | ih with (findIndex x xs) --
split on the result of
--
the recursive call, to
--
reduce case expression
--
in the IH type
-- in this case, the induction hypothesis takes the second
-- branch in the case expression in the type, which tells us
-- that Not (Elem x xs)
findIndexOk x (y :: xs) | No notHead | ih | Nothing =
notHeadNotTail notHead ih
-- in this case, the IH tells us that it was indeed in the tail,
-- so we wrap in There to make it valid for y :: xs instead of
-- just xs
findIndexOk x (y :: xs) | No notHead | ih | Just z = There ih
(in case my mail client is being silly with line wraps, here's a gist
link:
https://gist.github.com/david-christiansen/3b02d054b52405fecc70)
If that code doesn't make sense, I'd recommend working through the Idris
tutorial. If that doesn't clear things up, please ask further questions!
/David