In Idris, how can I enforce ordering in a data type for a binary search tree - idris

I'm having problems to enforce the ordering in a binary search tree in Idris, and I'm not even sure I'm using the right approach.
Coming from Liquid Haskell, the idea behind the implementation is creating a simple binary tree and then refining the types in the initial structure (as it can be seen here https://ucsd-progsys.github.io/liquidhaskell-tutorial/Tutorial_12_Case_Study_AVL.html).
When trying to translate the same ordering restriction to Idris, things get a bit thorny.
Here's what I'm doing, first, I start with a simple definition of a binary tree, same as Haskell.
data Tree : (a: Type) -> Type where
Leaf : Tree a
Node : (key: a) -> (left: Tree a) -> (right: Tree a) -> Tree a
Having this in hand (and following up this question: Encoding a binary search tree in data type), I constructed another data type that should enforce the "search" part of the binary search tree, as follows:
minVal : Ord a => Tree a -> Maybe a
minVal Leaf = Nothing
minVal (Node key Leaf _) = Just key
minVal (Node key left right) = minVal left
maxVal : Ord a => Tree a -> Maybe a
maxVal Leaf = Nothing
maxVal (Node key _ Leaf) = Just key
maxVal (Node key left right) = maxVal right
lessTree : Ord a => a -> Tree a -> Bool
lessTree x node = case minVal node of
Nothing => True
Just key => x < key
moreTree : Ord a => a -> Tree a -> Bool
moreTree x node = case maxVal node of
Nothing => True
Just key => x >= key
IsLft : Ord a => (x: a) -> (left: Tree a) -> Type
IsLft x left = So (moreTree x left)
IsRgt : Ord a => (x: a) -> (right: Tree a) -> Type
IsRgt x right = So (lessTree x right)
data IsBST : (t : Tree a) -> Type where
IsBSTLeaf : IsBST Leaf
IsBSTNode : Ord a => (x: a) -> (IsBST left) -> (IsLft x left) -> (IsBST right) -> (IsRgt x right) -> (IsBST (Node x left right))
BSTree : Type -> Type
BSTree a = (t : (Tree a) ** (IsBST t))
Basically, the "proof" that a BST is a BST would be that the left tree is also a BST and should be in the left side (as in its root value is smaller than its parent), and the same idea for the right side.
This code compiles, but it doesn't work as expected, it can be seen in the implementation of an insert function, as below:
data Comp = LT | EQ | GT
comp : Ord a => a -> a -> Comp
comp x y = if x > y then GT else if x < y then LT else EQ
wrongInsert : Ord a => (x : a) -> BSTree a -> BSTree a
wrongInsert x (Leaf ** IsBSTLeaf) =
let isLftPrf = mkIsLft x Leaf
isRgtPrf = mkIsRgt x Leaf
in ((Node x Leaf Leaf) ** (IsBSTNode x IsBSTLeaf isLftPrf IsBSTLeaf isRgtPrf))
wrongInsert x ((Node y left right) ** (IsBSTNode y lPrf isLftPrf rPrf isRgtPrf)) =
case comp y x of
LT =>
let (lTree ** pl) = wrongInsert x (left ** lPrf)
isLft = mkIsLft y lTree
in ((Node y lTree right) ** (IsBSTNode y pl isLft rPrf isRgtPrf))
GT =>
let (rTree ** pr) = wrongInsert x (right ** rPrf)
isRgt = mkIsRgt y rTree
in ((Node y left rTree) ** (IsBSTNode y lPrf isLftPrf pr isRgt))
EQ => ((Node y left right) ** (IsBSTNode y lPrf isLftPrf rPrf isRgtPrf))
In that case, the "LT" and "GT" parts are inverted, as in something greater than the root is being inserted in the left side, and vice-versa, so it shouldn't be possible to create such a tree, yet it works, which implies that Idris is accepting the creation of a "So False" (which I thought shouldn't be impossible, since there's only a constructor for True from what I've looked at).
From what I read on other sources, maybe using "So" is not the better idea here, but I fail to see other way to create a restriction in Idris as I have in Liquid Haskell, so that the data type itself prevents me from implementing a wrong insert function, as in the example above. Is that even possible, or if this approach is completely wrong, what would be the right way of implementing such a restriction?

Related

Is there a notion of "heterogenous collection of a given shape"?

A common pattern in functional programming languages with a sufficiently advanced type system to to have a type of "heterogeneous lists". For instance, given a list defined as:
data List a = Nil | Cons a (List a)
(Note: For concreteness, I will use Idris in this question, but this could also be answered in Haskell (with the right extensions), Agda, etc...)
We can define HList:
data HList : List a -> Type where
Nil : HList []
(::) : a -> HList as -> HList (a :: as)
This is a list which holds a different type (specified by the type-level List a) at each "position" of the List data type. This made me wonder: Can we generalize this construction? For instance, given a simple tree-like structure:
data Tree a = Branch a [Tree a]
Does it make sense to define a heterogenous tree?
where HTree : Tree a -> Type where
...
More generally in a dependently-typed language, is it possible to define a general construction:
data Hetero : (f : Type -> Type) -> f a -> Type where
....
that takes a data type of kind Type -> Type and returns the "heterogeneous container" of shape f? Has anyone made use of this construction before if possible?
We can talk about the shape of any functor using map and propositional equality. In Idris 2:
Hetero : (f : Type -> Type) -> Functor f => f Type -> Type
Hetero f tys = (x : f (A : Type ** A) ** map fst x = tys)
The type (A : Type ** A) is the type of non-empty types, in other words, values of arbitrary type. We get heterogeneous collections by putting arbitrarily typed values into functors, then constraining the types elementwise to particular types.
Some examples:
ex1 : Hetero List [Bool, Nat, Bool]
ex1 = ([(_ ** True), (_ ** 10), (_ ** False)] ** Refl)
data Tree : Type -> Type where
Leaf : a -> Tree a
Node : Tree a -> Tree a -> Tree a
Functor Tree where
map f (Leaf a) = Leaf (f a)
map f (Node l r) = Node (map f l) (map f r)
ex2 : Hetero Tree (Node (Leaf Bool) (Leaf Nat))
ex2 = (Node (Leaf (_ ** False)) (Leaf (_ ** 10)) ** Refl)

Theorem Proving in Idris

I was reading Idris tutorial. And I can't understand the following code.
disjoint : (n : Nat) -> Z = S n -> Void
disjoint n p = replace {P = disjointTy} p ()
where
disjointTy : Nat -> Type
disjointTy Z = ()
disjointTy (S k) = Void
So far, what I figure out is ...
Void is the empty type which is used to prove something is impossible.
replace : (x = y) -> P x -> P y
replace uses an equality proof to transform a predicate.
My questions are:
which one is an equality proof? (Z = S n)?
which one is a predicate? the disjointTy function?
What's the purpose of disjointTy? Does disjointTy Z = () means Z is in one Type "land" () and (S k) is in another land Void?
In what way can an Void output represent contradiction?
Ps. What I know about proving is "all things are no matched then it is false." or "find one thing that is contradictory"...
which one is an equality proof? (Z = S n)?
The p parameter is the equality proof here. p has type Z = S n.
which one is a predicate? the disjointTy function?
Yes, you are right.
What's the purpose of disjointTy?
Let me repeat the definition of disjointTy here:
disjointTy : Nat -> Type
disjointTy Z = ()
disjointTy (S k) = Void
The purpose of disjointTy is to be that predicate replace function needs. This consideration determines the type of disjointTy, viz. [domain] -> Type. Since we have equality between naturals numbers, [domain] is Nat.
To understand how the body has been constructed we need to take a look at replace one more time:
replace : (x = y) -> P x -> P y
Recall that we have p of Z = S n, so x from the above type is Z and y is S n. To call replace we need to construct a term of type P x, i.e. P Z in our case. This means the type P Z returns must be easily constructible, e.g. the unit type is the ideal candidate for this. We have justified disjointTy Z = () clause of the definition of disjointTy. Of course it's not the only option, we could have used any other inhabited (non-empty) type, like Bool or Nat, etc.
The return value in the second clause of disjointTy is obvious now -- we want replace to return a value of Void type, so P (S n) has to be Void.
Next, we use disjointTy like so:
replace {P = disjointTy} p ()
^ ^ ^
| | |
| | the value of `()` type
| |
| proof term of Z = S n
|
we are saying "this is the predicate"
As a bonus, here is an alternative proof:
disjoint : (n : Nat) -> Z = S n -> Void
disjoint n p = replace {P = disjointTy} p False
where
disjointTy : Nat -> Type
disjointTy Z = Bool
disjointTy (S k) = Void
I have used False, but could have used True -- it doesn't matter. What matters is our ability to construct a term of type disjointTy Z.
In what way can an Void output represent contradiction?
Void is defined like so:
data Void : Type where
It has no constructors! There is no way to create a term of this type whatsoever (under some conditions: like Idris' implementation is correct and the underlying logic of Idris is sane, etc.). So if some function claims it can return a term of type Void there must be something fishy going on. Our function says: if you give me a proof of Z = S n, I will return a term of the empty type. This means Z = S n cannot be constructed in the first place because it leads to a contradiction.
Yes, p : x = y is an equality proof. So p is a equality proof and Z = S k is a equality type.
Also yes, usually any P : a -> Type is called predicate, like IsSucc : Nat -> Type. In boolean logic, a predicate would map Nat to true or false. Here, a predicate holds, if we can construct a proof for it. And it is true, if we can construct it (prf : ItIsSucc 4). And it is false, if we cannot construct it (there is no member of ItIsSucc Z).
At the end, we want Void. Read the replace call as Z = S k -> disjointTy Z -> disjointTy (S k), that is Z = S K -> () -> Void. So replace needs two arguments: the proof p : Z = S k and the unit () : (), and voilĂ , we have a void. By the way, instead of () you could use any type that you can construct, e.g. disjointTy Z = Nat and then use Z instead of ().
In dependent type theory we construct proofs like prf : IsSucc 4. We would say, we have a proof prf that IsSucc 4 is true. prf is also called a witness for IsSucc 4. But with this alone we could only proove things to be true. This is the definiton for Void:
data Void : Type where
There is no constructor. So we cannot construct a witness that Void holds. If you somehow ended up with a prf : Void, something is wrong and you have a contradiction.

Totality and searching for elements in Streams

I want a find function for Streams of size-bounded types which is analogous to the find functions for Lists and Vects.
total
find : MaxBound a => (a -> Bool) -> Stream a -> Maybe a
The challenge is it to make it:
be total
consume no more than constant log_2 N space where N is the number of bits required to encode the largest a.
take no longer than a minute to check at compile time
impose no runtime cost
Generally a total find implementation for Streams sounds absurd. Streams are infinite and a predicate of const False would make the search go on forever. A nice way to handle this general case is the infinite fuel technique.
data Fuel = Dry | More (Lazy Fuel)
partial
forever : Fuel
forever = More forever
total
find : Fuel -> (a -> Bool) -> Stream a -> Maybe a
find Dry _ _ = Nothing
find (More fuel) f (value :: xs) = if f value
then Just value
else find fuel f xs
That works well for my use case, but I wonder if in certain specialized cases the totality checker could be convinced without using forever. Otherwise, somebody may suffer a boring life waiting for find forever ?predicateWhichHappensToAlwaysReturnFalse (iterate S Z) to finish.
Consider the special case where a is Bits32.
find32 : (Bits32 -> Bool) -> Stream Bits32 -> Maybe Bits32
find32 f (value :: xs) = if f value then Just value else find32 f xs
Two problems: it's not total and it can't possibly return Nothing even though there's a finite number of Bits32 inhabitants to try. Maybe I could use take (pow 2 32) to build a List and then use List's find...uh, wait...the list alone would take up GBs of space.
In principle it doesn't seem like this should be difficult. There's finitely many inhabitants to try, and a modern computer can iterate through all 32-bit permutations in seconds. Is there a way to have the totality checker verify the (Stream Bits32) $ iterate (+1) 0 eventually cycles back to 0 and once it does assert that all the elements have been tried since (+1) is pure?
Here's a start, although I'm unsure how to fill the holes and specialize find enough to make it total. Maybe an interface would help?
total
IsCyclic : (init : a) -> (succ : a -> a) -> Type
data FinStream : Type -> Type where
MkFinStream : (init : a) ->
(succ : a -> a) ->
{prf : IsCyclic init succ} ->
FinStream a
partial
find : Eq a => (a -> Bool) -> FinStream a -> Maybe a
find pred (MkFinStream {prf} init succ) = if pred init
then Just init
else find' (succ init)
where
partial
find' : a -> Maybe a
find' x = if x == init
then Nothing
else
if pred x
then Just x
else find' (succ x)
total
all32bits : FinStream Bits32
all32bits = MkFinStream 0 (+1) {prf=?prf}
Is there a way to tell the totality checker to use infinite fuel verifying a search over a particular stream is total?
Let's define what it means for a sequence to be cyclic:
%default total
iter : (n : Nat) -> (a -> a) -> (a -> a)
iter Z f = id
iter (S k) f = f . iter k f
isCyclic : (init : a) -> (next : a -> a) -> Type
isCyclic init next = DPair (Nat, Nat) $ \(m, n) => (m `LT` n, iter m next init = iter n next init)
The above means that we have a situation which can be depicted as follows:
-- x0 -> x1 -> ... -> xm -> ... -> x(n-1) --
-- ^ |
-- |---------------------
where m is strictly less than n (but m can be equal to zero). n is some number of steps after which we get an element of the sequence we previously encountered.
data FinStream : Type -> Type where
MkFinStream : (init : a) ->
(next : a -> a) ->
{prf : isCyclic init next} ->
FinStream a
Next, let's define a helper function, which uses an upper bound called fuel to break out from the loop:
findLimited : (p : a -> Bool) -> (next : a -> a) -> (init : a) -> (fuel : Nat) -> Maybe a
findLimited p next x Z = Nothing
findLimited p next x (S k) = if p x then Just x
else findLimited pred next (next x) k
Now find can be defined like so:
find : (a -> Bool) -> FinStream a -> Maybe a
find p (MkFinStream init next {prf = ((_,n) ** _)}) =
findLimited p next init n
Here are some tests:
-- I don't have patience to wait until all32bits typechecks
all8bits : FinStream Bits8
all8bits = MkFinStream 0 (+1) {prf=((0, 256) ** (LTESucc LTEZero, Refl))}
exampleNothing : Maybe Bits8
exampleNothing = find (const False) all8bits -- Nothing
exampleChosenByFairDiceRoll : Maybe Bits8
exampleChosenByFairDiceRoll = find ((==) 4) all8bits -- Just 4
exampleLast : Maybe Bits8
exampleLast = find ((==) 255) all8bits -- Just 255

Idris: proof that specific terms are impossible

Idris version: 0.9.16
I am attempting to describe constructions generated from a base value and an iterated step function:
namespace Iterate
data Iterate : (base : a) -> (step : a -> a) -> a -> Type where
IBase : Iterate base step base
IStep : Iterate base step v -> Iterate base step (step v)
Using this I can define Plus, describing constructs from iterated addition of a jump value:
namespace Plus
Plus : (base : Nat) -> (jump : Nat) -> Nat -> Type
Plus base jump = Iterate base (\v => jump + v)
Simple example uses of this:
namespace PlusExamples
Even : Nat -> Type; Even = Plus 0 2
even0 : Even 0; even0 = IBase
even2 : Even 2; even2 = IStep even0
even4 : Even 4; even4 = IStep even2
Odd : Nat -> Type; Odd = Plus 1 2
odd1 : Odd 1; odd1 = IBase
odd3 : Odd 3; odd3 = IStep odd1
Fizz : Nat -> Type; Fizz = Plus 0 3
fizz0 : Fizz 0; fizz0 = IBase
fizz3 : Fizz 3; fizz3 = IStep fizz0
fizz6 : Fizz 6; fizz6 = IStep fizz3
Buzz : Nat -> Type; Buzz = Plus 0 5
buzz0 : Buzz 0; buzz0 = IBase
buzz5 : Buzz 5; buzz5 = IStep buzz0
buzz10 : Buzz 10; buzz10 = IStep buzz5
The following describes that values below the base are impossible:
noLess : (base : Nat) ->
(i : Fin base) ->
Plus base jump (finToNat i) ->
Void
noLess Z FZ m impossible
noLess (S b) FZ IBase impossible
noLess (S b) (FS i) IBase impossible
And the following for values between base and jump + base:
noBetween : (base : Nat) ->
(predJump : Nat) ->
(i : Fin predJump) ->
Plus base (S predJump) (base + S (finToNat i)) ->
Void
noBetween b Z FZ m impossible
noBetween b (S s) FZ IBase impossible
noBetween b (S s) (FS i) IBase impossible
I am having trouble defining the following function:
noJump : (Plus base jump n -> Void) -> Plus base jump (jump + n) -> Void
noJump f m = ?noJump_rhs
That is: if n isn't base plus a natural multiple of jump, then neither is jump + n.
If I ask Idris to case split m it only shows me IBase - then I get stuck.
Would someone point me in the right direction?
Edit 0:
Applying induction to m gives me the following message:
Induction needs an eliminator for Iterate.Iterate.Iterate
Edit 1:
Name updates and here is a copy of the source: http://lpaste.net/125873
I think there's a good reason to get stuck on the IBase case of this proof, which is that the theorem is false! Consider:
noplus532 : Plus 5 3 2 -> Void
noplus532 IBase impossible
noplus532 (IStep _) impossible
plus535 : Plus 5 3 (3 + 2)
plus535 = IBase
To Edit 0: to induct on a type, it needs a special qualifier:
%elim data Iterate = <your definition>
To the main question: sorry that I haven't read through all your code, I only want to make some suggestion for falsifying proofs. From my experience (I even delved the standard library sources to find out some help), when you need to prove Not a (a -> Void), often you can use some Not b (b -> Void) and a way to convert a to b, then just pass it to the second proof. For example, a very simple proof that one list cannot be prefix of another if they have different heads:
%elim data Prefix : List a -> List a -> Type where
pEmpty : Prefix Nil ys
pNext : Prefix xs ys -> Prefix (x :: xs) (x :: ys)
prefixNotCons : Not (x = y) -> Not (Prefix (x :: xs) (y :: ys))
prefixNotCons r (pNext _) = r refl
In your case, I suppose you need to combine several proofs.

Doing rank-n quantification in Idris

I can only do rank-n types in Idris 0.9.12 in a rather clumsy way:
tupleId : ((a : Type) -> a -> a) -> (a, b) -> (a, b)
tupleId f (a, b) = (f _ a, f _ b)
I need the underscores wherever there's a type application, because Idris throws parse errors when I try to make the (nested) type arguments implicit:
tupleId : ({a : Type} -> a -> a) -> (a, b) -> (a, b) -- doesn't compile
A probably bigger issue is that I can't do class constraints in higher-rank types at all. I can't translate the following Haskell function to Idris:
appShow :: Show a => (forall a. Show a => a -> String) -> a -> String
appShow show x = show x
This also prevents me from using Idris functions as type synonyms for types such as Lens, which is Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t in Haskell.
Any way to remedy or circumvent the above issues?
I've just implemented this in master, allowing implicits in arbitrary scopes, and it'll be in the next hackage release. It's not well tested yet though! I have at least tried the following simple examples, and a few others:
appShow : Show a => ({b : _} -> Show b => b -> String) -> a -> String
appShow s x = s x
AppendType : Type
AppendType = {a, n, m : _} -> Vect n a -> Vect m a -> Vect (n + m) a
append : AppendType
append [] ys = ys
append (x :: xs) ys = x :: append xs ys
tupleId : ({a : _} -> a -> a) -> (a, b) -> (a, b)
tupleId f (a, b) = (f a, f b)
Proxy : Type -> Type -> Type -> Type -> (Type -> Type) -> Type -> Type
Producer' : Type -> (Type -> Type) -> Type -> Type
Producer' a m t = {x', x : _} -> Proxy x' x () a m t
yield : Monad m => a -> Producer' a m ()
The main constraint at the minute is that you can't give values for implicit arguments directly, except at the top level. I'll do something about that eventually...