Problem with dependent types function in Idris - idris

I have just started reading a book "Type-driven development" and tried my simple example with dependent types. It should return a string for negative numbers and Integer for the positive ones.
I started with 2 holes:
StringOrInt : Bool -> Type
StringOrInt b =
case b of
True => Integer
False => String
getStringOrInt : (x : Integer) -> StringOrInt (x > 0)
getStringOrInt x =
case x > 0 of
True => ?x
False => ?s
If I take a look at holes definition it looks very complicated and not helpful at all:
x : case with block in Prelude.Interfaces.Prelude.Interfaces.Integer implementation of Prelude.Interfaces.Ord, method > (ifThenElse (intToBool (prim__eqBigInt x 0))
(Delay EQ)
(Delay (ifThenElse (intToBool (prim__sltBigInt x
0))
(Delay LT)
(Delay GT))))
x
0 of
True => Integer
False => String
So how to write this function?

Use with rather than case to leverage dependent pattern matching and have the type checker substitute the appropriate Boolean for x > 0 in the result type for each alternative:
StringOrInt : Bool -> Type
StringOrInt True = Integer
StringOrInt False = String
getStringOrInt : (x : Integer) -> StringOrInt (x > 0)
getStringOrInt x with (x > 0)
getStringOrInt x | True = x
getStringOrInt x | False = "<= 0"

Related

Pattern-match (destructure) in equality proof

data T = A String | B String
p : ((A s) = (A s')) -> (s = s')
If I have (A s) = (A s'), how do I obtain s = s'?
P.S. I'm new to Idris. Feel free to edit my question for code style or to add pertinent keywords.
Pattern match on Refl:
data T = A String | B String
p : ((A s) = (A s')) -> (s = s')
p Refl = Refl

Create simple functions with dependent types

I am learning from the official documentation. I have tried to modify the first example:
isSingleton : Bool -> Type
isSingleton True = Nat
isSingleton False = List Nat
mkSingle : (x : Bool) -> isSingleton x
mkSingle True = 0
mkSingle False = []
Here are my tries:
compute_type : Integer -> Type
compute_type 1 = Nat
compute_type 2 = String
--foo : (x : Integer) -> compute_type x
-- foo 1 = Z
-- foo 2 = ?one --unwords ["A", "B", "C"]
Error:
{-
first_class_type.idr:7:13:
|
7 | foo 1 = Z
| ^
When checking right hand side of foo with expected type
compute_type 1
Type mismatch between
Nat (Type of 0)
and
compute_type 1 (Expected type)
-}
ct2 : String -> Type
ct2 "Integer" = Integer
ct2 "String" = String
foo2 : (s : String) -> ct2 s
-- foo2 "Integer" = 42
Error:
{-
|
28 | foo2 "Integer" = 42
| ~~
When checking right hand side of foo2 with expected type
ct2 "Integer"
ct2 "Integer" is not a numeric type
-}
ct3: Bool -> Bool -> Type
ct3 True True = Nat
ct3 False False = String
foo3: (a: Bool) -> (b: Bool) -> (ct3 a b)
-- foo3 True True = Z
-- foo3 False False = "Stringggg"
Error:
{-
first_class_type.idr:44:18:
|
44 | foo3 True True = Z
| ^
When checking right hand side of foo3 with expected typ
ct3 True True
Type mismatch between
Nat (Type of 0)
and
ct3 True True (Expected type)
Holes: Main.foo3, Main.foo2
-}
ct4: (b: String) -> Type
ct4 "Integer" = Integer
ct4 "String" = String
foo4: (s: String) -> ct4 s -> Integer
-- foo4 "Integer" x = x
-- foo4 "String" ss = 987
Error:
{-
|
67 | foo4 "Integer" x = x
| ^
When checking right hand side of foo4 with expected type
Integer
Type mismatch between
ct4 "Integer" (Type of x)
and
Integer (Expected type)
-}
I have no idea why my functions do not returns types. They look similar to Idris codes but they do not work.
Your type functions are not total. You can check this with :total foo or specifying, that the function should be total:
%default total
-- or
total foo : Integer -> Type
Only total functions are get resolved in the type checker, otherwise it may or may not run forever. If you want to stick to Integer and String, to make your functions total, you can just add a default case:
compute_type : Integer -> Type
compute_type 1 = Nat
compute_type _ = String
foo : compute_type 1
foo = Z

Idris proof of less than

I'm very new to Idris (and dependent types). I was trying to do write a program to check if a string is a palindrome of not. To do that, I decided to compute the length of the string, and compute
q,r = (strlen `div` 2, strlen `mod` 2)
and then split the string as follows:
lhalf,rhalf = (substr 0 (q+r) str, substr (q-r) (q+r) str)
This takes care of both odd and even length strings. The problem is that Idris needs a proof that r < q since both q and r are Nat.
My question is: How do I express the fact that r
Here's the full sample of my code:
module Main
isPalindrome : (str : String) -> String
isPalindrome str =
let split = half_half str
in show ((fst split) == reverse (snd split))
where
strlen : Nat
strlen = length str
divMod : Nat -> Nat -> (Nat,Nat)
divMod x y = (x `div` y, x `mod` y)
half_half : String -> (String, String)
half_half "" = ("","")
half_half x = let
(q,r) = divMod strlen 2
in
(substr 0 (q+r) x,
substr (q-r) (q+r) x)
main : IO ()
main = repl "> " isPalindrome
You can't proof that r ≤ q because it's not true. For example, given the string "a" you have strlen = 1 and therefore q = 0 and r = 1. In this example r ≤ q is clearly false.
Note that you can implement isPalindrome simply by
isPalindrome: String -> Bool
isPalindrome str = str == reverse str

Tell dependent function in conditional statement branch that condition is true

I have a function with a type signature (x, y : SomeType) -> (cond x y) = True -> SomeType. When I check the condition in if-then-else/case/with statement, how do I pass to the function in a corresponding branch the fact, that condition is true?
You can use DecEq to make this easy:
add : (x, y : Nat) -> x + y < 10 = True -> Nat
add x y _ = x + y
main : IO ()
main =
let x = S Z
in let y = Z
in case decEq (x + y < 10) True of
Yes prf => print (add x y prf)
No _ => putStrLn "x + y is not less than 10"
But you shouldn't.
Using booleans (via = or So) can tell you that something is true, but not why. The why is very important if you want to compose proofs together or break them apart. Imagine if add called a function which needed x + y < 20 - we can't just pass our proof that x + y < 10 = True because Idris knows nothing about the operation, just that it's true.
Instead, you should write the above with a data type which contains why it's true. LTE is a type which does that for less-than comparisons:
add : (x, y : Nat) -> LTE (x + y) 10 -> Nat
add x y _ = x + y
main : IO ()
main =
let x = S Z
in let y = Z
in case isLTE (x + y) 10 of
Yes prf => print (add x y prf)
No _ => putStrLn "x + y is not less than 10"
Now, if add called a function which needed a LTE (x + y) 20 we can write a function to widen the constraint:
widen : a `LTE` b -> (c : Nat) -> a `LTE` (b + c)
widen LTEZero c = LTEZero
widen (LTESucc x) c = LTESucc (widen x c)
This is not something we can easily do with just booleans.

Multiple Conditionals in Poly ML

If, for example, I wanted to define a function that returned true if a=b and b=c, and false if neither one of those equalities were true in Poly ML, how would I write it? I'm not sure how to do more than one conditional simultaneously.
Isn't
a = b andalso b = c
what you want?
I believe this does what you need:
fun f (a, b, c) =
if a = b andalso b = c
then true
else
if a <> b andalso b <> c
then false
else ... (* you haven't specified this case *)
The main points here are:
You can nest conditionals, i.e. have one if expression inside another's then or else case
The operator andalso is the boolean conjunction, meaning x andalso y is true if and only if x evaluates to true and y evaluates to true.
You can put this more concisely using a case expression:
fun f (a, b, c) =
case (a = b, b = c) of
(true, true) => true
| (false, false) => false
| _ => (* you haven't specified this case *)