What kind of things can one express with Dec and not with Maybe in Idris?
Or in other words: When should one choose Dec and when Maybe?
I told a little bit about this in the answer to the one recent question.
There're two reasons for using Dec:
You want to prove things and have more guarantees from the implementation.
You want your function to run in finite time.
Regarding 1. Consider this function for Nat equality:
natEq : (n: Nat) -> (m: Nat) -> Maybe (n = m)
natEq Z Z = Just Refl
natEq Z (S k) = Nothing
natEq (S k) Z = Nothing
natEq (S k) (S j) = case natEq k j of
Nothing => Nothing
Just Refl => Just Refl
You can write tests for this function and see if it works. But compiler can't stop you at compile-time from writing Nothing in every case. Such function still will be compilable. Maybe is some sort of weak proof. It means, if you return Just then you're able to find answer and we are good but if you return Nothing it means nothing. You just can't find answer. But when you're using Dec you can't just return No. Because if you're returning No it means that you can actually prove that there is no answer. So rewriting natEq into Dec will require more effort from you as a programmer but implementation is now more robust:
zeroNotSucc : (0 = S k) -> Void
zeroNotSucc Refl impossible
succNotZero : (S k = 0) -> Void
succNotZero Refl impossible
noNatEqRec : (contra : (k = j) -> Void) -> (S k = S j) -> Void
noNatEqRec contra Refl = contra Refl
natEqDec : (n: Nat) -> (m: Nat) -> Dec (n = m)
natEqDec Z Z = Yes Refl
natEqDec Z (S k) = No zeroNotSucc
natEqDec (S k) Z = No succNotZero
natEqDec (S k) (S j) = case natEqDec k j of
Yes Refl => Yes Refl
No contra => No (noNatEqRec contra)
Regarding 2. Dec stands for decidability. It means that you can return Dec only for decidable problems, i.e. problems that can be solved in finite time. You can solve Nat equality in finite time because you'll eventually fall into case with Z. But for something difficult, like, check whether given String represents Idris program that calculates first 10 prime numbers, you can't.
Related
I am just starting to learn Idris coming from Haskell, and I'm trying to write some simple linear algebra code.
I want to write a Num interface instance for Vect, but specifically for Vect n a with the constraint that a has a Num instance.
In Haskell I would write a typeclass instance like this:
instance Num a => Num (Vect n a) where
(+) a b = (+) <$> a <*> b
(*) a b = (*) <$> a <*> b
fromInteger a = a : Nil
But reading the Idris interface docs does not seem to mention constraints on interface instances.
The best I can do is the following, which predictably causes the compiler to lament about a not being a numeric type:
Num (Vect n a) where
(+) Nil Nil = Nil
(+) (x :: xs) (y :: ys) = x + y :: xs + ys
(*) Nil Nil = Nil
(*) (x :: xs) (y :: ys) = x * y :: xs * ys
fromInteger i = Vect 1 (fromInteger i)
I can work around this by creating my own vector type with a Num constraint (which isn't portable) or by overloading (+) in a namespace (which feels a little clunky):
namespace Vect
(+) : Num a => Vect n a -> Vect n a -> Vect n a
(+) xs ys = (+) <$> xs <*> ys
Is there a way to constrain interface implementations, or is there a better way to accomplish this, eg using dependent types?
In Idris, you'd do (almost) the same as haskell
Num a => Num (Vect n a) where
Like a number of things, this is in the book but not, apparently, in the docs.
Trying to prove the following assertion:
equalityCommutesNat : (n : Nat) -> (m : Nat) -> n = m -> m = n
I found plusCommutes in the libraries but nothing for equality.
The only inhabitant of = is Refl : (a = a), so if you pattern match, you'll get evidence that n is m.
Which means you can then use Refl, since Idris's pattern matching now knows they're the same:
equalityCommutesNat : (n : Nat) -> (m : Nat) -> n = m -> m = n
equalityCommutesNat _ _ Refl = Refl
And you can play around with this in the REPL:
> equalityCommutesNat 1 1 Refl
Refl : 1 = 1
In the following code (which is an attempt to solve an exercise from 'Software Foundations' [chapter on Lists]), Idris reports a very complex type for countSingleton_rhs. The type includes a complex expression having the following at its core: case decEq x x of ....
module CountSingleton
data NatList : Type where
Nil : NatList
(::) : Nat -> NatList -> NatList
-- count occurrences of a value in a list
count : (v : Nat) -> (s : NatList) -> Nat
count _ [] = Z
count Z (Z :: ns) = S (count Z ns)
count Z (_ :: ns) = count Z ns
count j#(S _) (Z :: ns) = count j ns
count (S j) ((S k) :: ns) =
case decEq j k of
Yes Refl => S (count (S j) ns)
No _ => count (S j) ns
-- to prove
countSingleton : (v : Nat) -> (count v [v]) = S Z
countSingleton Z = Refl
countSingleton (S k) = ?countSingleton_rhs
Why isn't Idris simplifying decEq x x to Yes Refl?
Is there a better way to implement count which avoids this behaviour?
What can I do to simplify/rewrite the types in order to make progress?
Your count function is more splitted than it needs to. If you check for decEq x y anyway, you can unify all cases except count _ [] = Z:
count : (v : Nat) -> (s : NatList) -> Nat
count _ [] = Z
count x (y :: ns) = case decEq x y of
Yes Refl => S (count x ns)
No _ => count x ns
The straight-forward way to prove countSingleton is to follow the flow. Your countSingleton_rhs has a complex type, because the type is a case switch, depending on the result of decEq v v. Using with Idris can apply the result of the branch to the resulting type.
countSingleton : (v : Nat) -> (count v [v]) = S Z
countSingleton v with (decEq v v)
| Yes prf = Refl
| No contra = absurd $ contra Refl
As you have noted, this seems a bit redundant, as decEq x x is clearly Yes Refl. Luckily it is already proven in the library: decEqSelfIsYes : DecEq a => decEq x x = Yes Refl, which we can use to rewrite the resulting type:
countSingleton : (v : Nat) -> (count v [v]) = S Z
countSingleton v = rewrite decEqSelfIsYes {x=v} in Refl
Unfortunately because of an open issue, rewriting case types doesn't always work. But you can just rewrite count with with to circumvent this issue:
count : (v : Nat) -> (s : NatList) -> Nat
count _ [] = Z
count x (y :: ns) with (decEq x y)
| Yes _ = S (count x ns)
| No _ = count x ns
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).
Suppose I define my own list type.
data MyVec : Nat -> Type -> Type where
MyNil : MyVec Z a
(::) : a -> MyVec k a -> MyVec (S k) a
And a myMap function serving as fmap for MyVec:
myMap : (a -> b) -> MyVec k a -> MyVec k b
myMap f MyNil = ?rhs_nil
myMap f (x :: xs) = ?rhs_cons
Attempting to solve the ?rhs_nil hole in my mind:
:t ?rhs_nil is MyVec 0 b
fair enough - I need a constructor that returns MyVec parameterized by b and I need k to be 0 (or Z) and I can see that MyNil is indexed by Z and parameterized by whatever, so I can use b easily, therefore ?rhs_nil = MyNil and it typechecks, dandy
:t ?rhs_cons is MyVec (S k)
I need a constructor that returns MyVec parameterized by b and I need k to be (S k)
I do see that the constructor (::) constructs a list indexed by (S k) and I try to use it. The first argument needs to be of type b considering I am building a MyVec <> b and the only way to get it is to apply x to f.
myMap f (x :: xs) = f x :: <>
Now I get confused. The RHS of (::) is supposed to be MyVec k b, why can I not simply use the MyNil constructor, with unification / substitution k == Z (that MyNil) gives me, getting:
myMap f (x :: xs) = f x :: MyNil
I do understand that I need to recurse and have = f x :: myMap f xs, but how does the compiler know the number of times the (::) constructor needs to be applied? How does it infer the correct k for this case, preventing me from using Z there.
The k is already implied by xs : MyVec k a. So you cannot unify k with Z if xs contains some elements.