How to specify a number range as a type in Idris? - dependent-type

I've been experimenting with Idris and it seems like it should be simple to specify some sort of type for representing all numbers between two different numbers, e.g. NumRange 5 10 is the type of all numbers between 5 and 10. I'd like to include doubles/floats, but a type for doing the same with integers would be equally useful. How would I go about doing this?

In practice, you may do better to simply check the bounds as needed, but you can certainly write a data type to enforce such a property.
One straightforward way to do it is like this:
data Range : Ord a => a -> a -> Type where
MkRange : Ord a => (x,y,z : a) -> (x >= y && (x <= z) = True) -> Range y z
I've written it generically over the Ord typeclass, though you may need to specialize it. The range requirement is expressed as an equation, so you simply supply a Refl when constructing it, and the property will then be checked. For example: MkRange 3 0 10 Refl : Range 0 10. One disadvantage of something like this is the inconvenience of having to extract the contained value. And of course if you want to construct an instance programmatically you'll need to supply the proofs that the bounds are indeed satisfied, or else do it in some context that allows for failure, like Maybe.
We can write a more elegant example for Nats without much trouble, since for them we already have a library data type to represent comparison proofs. In particular LTE, representing less-than-or-equal-to.
data InRange : Nat -> Nat -> Type where
IsInRange : (x : Nat) -> LTE n x -> LTE x m -> InRange n m
Now this data type nicely encapsulates a proof that n ≤ x ≤ m. It would be overkill for many casual applications, but it certainly shows how you might use dependent types for this purpose.

Related

Pointwise function equality proof [duplicate]

I'm just starting playing with idris and theorem proving in general. I can follow most of the examples of proofs of basic facts on the internet, so I wanted to try something arbitrary by my own. So, I want to write a proof term for the following basic property of map:
map : (a -> b) -> List a -> List b
prf : map id = id
Intuitively, I can imagine how the proof should work: Take an arbitrary list l and analyze the possibilities for map id l. When l is empty, it's obvious; when
l is non-empty it's based on the concept that function application preserves equality.
So, I can do something like this:
prf' : (l : List a) -> map id l = id l
It's like a for all statement. How can I turn it into a proof of the equality of the functions involved?
You can't. Idris's type theory (like Coq's and Agda's) does not support general extensionality. Given two functions f and g that "act the same", you will never be able to prove Not (f = g), but you will only be able to prove f = g if f and g are defined the same, up to alpha and eta equivalence or so. Unfortunately, things only get worse when you consider higher-order functions; there's a theorem about such in the Coq standard library, but I can't seem to find or remember it right now.

Where is the Idris == operator useful?

As a beginner in type-driven programming, I'm curious about the use of the == operator. Examples demonstrate that it's not sufficient to prove equality between two values of a certain type, and special equality checking types are introduced for the particular data types. In that case, where is == useful at all?
(==) (as the single constituent function of the Eq interface) is a function from a type T to Bool, and is good for equational reasoning. Whereas x = y (where x : T and y : T) AKA "intensional equality" is itself a type and therefore a proposition. You can and often will want to bounce back and forth between the two different ways of expressing equality for a particular type.
x == y = True is also a proposition, and is often an intermediate step between reasoning about (==) and reasoning about =.
The exact relationship between the two types of equality is rather complex, and you can read https://github.com/pdorrell/learning-idris/blob/9d3454a77f6e21cd476bd17c0bfd2a8a41f382b7/finished/EqFromEquality.idr for my own attempt to understand some aspects of it. (One thing to note is that even though an inductively defined type will have decideable intensional equality, you still have to go through a few hoops to prove that, and a few more hoops to define a corresponding implementation of Eq.)
One particular handy code snippet is this:
-- for rel x y, provide both the computed value, and the proposition that it is equal to the value (as a dependent pair)
has_value_dpair : (rel : t -> t -> Bool) -> (x : t) -> (y : t) -> (value: Bool ** rel x y = value)
has_value_dpair rel x y = (rel x y ** Refl)
You can use it with the with construct when you have a value returned from rel x y and you want to reason about the proposition rel x y = True or rel x y = False (and rel is some function that might represent a notion of equality between x and y).
(In this answer I assume the case where (==) corresponds to =, but you are entirely free to define a (==) function that doesn't correspond to =, eg when defining a Setoid. So that's another reason to use (==) instead of =.)
You still need good old equality because sometimes you can't prove things. Sometimes you don't even need to prove. Consider next example:
countEquals : Eq a => a -> List a -> Nat
countEquals x = length . filter (== x)
You might want to just count number of equal elements to show some statistics to user. Another example: tests. Yes, even with strong type system and dependent types you might want to perform good old unit tests. So you want to check for expectations and this is rather convenient to do with (==) operator.
I'm not going to write full list of cases where you might need (==). Equality operator is not enough for proving but you don't always need proofs.

Why is this mutually recursive data definition not total and how can I fix it?

I am experimenting with Idris a lot lately and came up with the following "type level definition of a set":
mutual
data Set : Type -> Type where
Empty : Set a
Insert : (x : a) -> (xs : Set a) -> Not (Elem x xs) -> Set a
data Elem : (x : a) -> Set a -> Type where
Here : Elem x (Insert x xs p)
There : Elem x xs -> Elem x (Insert y xs p)
So a set is either empty or it consists of a set and an additional element that is proven not to be in that set already.
When I totality check this, I get the error
[...] is not strictly positive
for Insert, Here and There. I have been searching the documentation for terms like "strictly positive" and the totality checker in general, but I cannot figure out why this case in particular is not total (or strictly positive). Can somebody explain this?
The natural next question then is of course how to "fix" it. Can I somehow change the definition, keeping its semantics, so that it totality checks?
Since I don't really need the definition to look like this (it is only an experiment after all) it would also be interesting to know whether there is another, somehow more idiomatic way to represent Sets at the type level that is total.
This SO post explains what strictly positive types are and why they matter. In your case, since Not (Elem x xs) simply means a function Elem x xs -> Void, this is where the "type being defined occurring on the left-hand side of an arrow" comes from.
Can you make do with something like this?
mutual
data Set : Type -> Type where
Empty : Set a
Insert : (x : a) -> (xs : Set a) -> NotElem x xs -> Set a
data NotElem : (x : a) -> Set a -> Type where
NotInEmpty : NotElem x Empty
NotInInsert : Not (x = y) -> NotElem x ys -> NotElem x (Insert y ys p)

How to "read" Elm's -> operator

I'm really loving Elm, up to the point where I encounter a function I've never seen before and want to understand its inputs and outputs.
Take the declaration of foldl for example:
foldl : (a -> b -> b) -> b -> List a -> b
I look at this and can't help feeling as if there's a set of parentheses that I'm missing, or some other subtlety about the associativity of this operator (for which I can't find any explicit documentation). Perhaps it's just a matter of using the language more until I just get a "feel" for it, but I'd like to think there's a way to "read" this definition in English.
Looking at the example from the docs…
foldl (::) [] [1,2,3] == [3,2,1]
I expect the function signature to read something like this:
Given a function that takes an a and a b and returns a b, an additional b, and a List, foldl returns a b.
Is that correct?
What advice can you give to someone like me who desperately wants inputs to be comma-delineated and inputs/outputs to be separated more clearly?
Short answer
The missing parentheses you're looking for are due to the fact that -> is right-associative: the type (a -> b -> b) -> b -> List a -> b is equivalent to (a -> b -> b) -> (b -> (List a -> b)). Informally, in a chain of ->s, read everything before the last -> as an argument and only the rightmost thing as a result.
Long answer
The key insight you may be missing is currying -- the idea that if you have a function that takes two arguments, you can represent it with a function that takes the first argument and returns a function that takes the second argument and then returns the result.
For instance, suppose you have a function add that takes two integers and adds them together. In Elm, you could write a function that takes both elements as a tuple and adds them:
add : (Int, Int) -> Int
add (x, y) = x+y
and you could call it as
add (1, 2) -- evaluates to 3
But suppose you didn't have tuples. You might think that there would be no way to write this function, but in fact using currying you could write it as:
add : Int -> (Int -> Int)
add x =
let addx : Int -> Int
addx y = x+y
in
addx
That is, you write a function that takes x and returns another function that takes y and adds it to the original x. You could call it with
((add 1) 2) -- evaluates to 3
You can now think of add in two ways: either as a function that takes an x and a y and adds them, or as a "factory" function that takes x values and produces new, specialized addx functions that take just one argument and add it to x.
The "factory" way of thinking about things comes in handy every once in a while. For instance, if you have a list of numbers called numbers and you want to add 3 to each number, you can just call List.map (add 3) numbers; if you'd written the tuple version instead you'd have to write something like List.map (\y -> add (3,y)) numbers which is a bit more awkward.
Elm comes from a tradition of programming languages that really like this way of thinking about functions and encourage it where possible, so Elm's syntax for functions is designed to make it easy. To that end, -> is right-associative: a -> b -> c is equivalent to a -> (b -> c). This means if you don't parenthesize, what you're defining is a function that takes an a and returns a b -> c, which again we can think of either as a function that takes an a and a b and returns a c, or equivalently a function that takes an a and returns a b -> c.
There's another syntactic nicety that helps call these functions: function application is left-associative. That way, the ugly ((add 1) 2) from above can be written as add 1 2. With that syntax tweak, you don't have to think about currying at all unless you want to partially apply a function -- just call it with all the arguments and the syntax will work out.

Decidability of propositional equaility

Two terms in agda are said to be definitionally equal precisely when they both have the same normal form ---I think---, and propositional equality is just the data-type representation of definitional equality ---again, I guess---; so then shouldn't propositionally equality be decidable? That is, would it seem reasonable that we can write a function typed
∀{A : Set} → (x y : A) → Dec(x ≡ y).
I kinda of get that we cannot write such a function since we cannot pattern match on the arguments, but I 'feel' that it should be possible: again, just reduce to normal form and check for syntactic identity.
Any insight would be helpful!
Two terms in agda are said to be definitionally equal precisely when
they both have the same normal form
Up to αη-conversion.
and propositional equality is just the data-type representation of definitional equality
Propositional equality says "these two terms will become definitionally equal after you instantiate some free variables" ("some" can be 0 or all of them) ("instantiate" can vary too).
E.g.
double-double : (n : ℕ) -> n + n ≡ 2 * n
clearly, n + n is not syntactically equal to 2 * n, but for any canonical n (0, 1, 2...) the result of n + n is syntactically equal to the result of 2 * n — that's what double-double says. And that "for any canonical n" part forces us to prove double-double by induction (though, in a more complicated system, where definitional equality is based on supercompilation or there is a build-in prover, n + n is definitionally equal to 2 * n).
But sometimes it's not as obvious how an induction hypothesis should look like, e.g. when you need to generalize an equation. As you might expect, there is no decision procedure for "is this arbitrary thing provable?" and hence propositional equality is undecidable. Moreover, you can't neither prove nor disprove statements like
(λ n -> 1 + n) ≡ (λ n -> n + 1)
without additional postulates.
However you really can check for syntactic equality:
_≟_ : ∀ {α} {A : Set α} -> (x y : A) -> Maybe (x ≡ y)
It says "if two terms are syntactically equal, then they are equal propositionally, otherwise we don't know whether they are equal propositionally or not". But Agda doesn't have such built-in function.