How can I learn more about how type checking happens in Idris? - idris

I am learning programming using dependent types and started using Idris recently. I have some background in programming languages and understand how type checking happens in languages like Haskell and ML. I wanted to know if there is some description of how type checking happens in Idris, even a flag that can be passed to the Idris compiler which dumps the output of the type checker will also be useful.
For example, I want to understand how Idris type checks the correctness of the function append,
append : Vect m elem -> Vect n elem -> Vect (m + n) elem
append [] ys = ys
append (x::xs) ys = x :: (append xs ys)
The reason is that Idris gives a type error when I wrongly define append as append (x::xs) ys = x :: (append ys xs), I interchanged the arguments in the recursive call, saying that (plus k n) is not equal to (plus n k).
My guess is that even if I fix this by giving a proof that plus is commutative, Idris must still give a type error. Therefore, I want to understand type checking in Idris and see if it really happens and if so how it figures out. Thanks in advance.

Related

Equality in Agda - irrelevant arguments

I have a dependant type that consists of a value plus some proofs about its properties. Naturally I would like my notion of equality over this type to be equivalent to equality over the value component. This is all fine except that I run into problems when proving properties of lifted notions of this equality (for example equality over lists of this type).
For example:
open import Data.Nat
open import Data.Product
open import Relation.Binary
open import Relation.Binary.PropositionalEquality
open import Relation.Binary.List.Pointwise renaming (Rel to ListRel) hiding (refl)
module Test where
_≈_ : Rel (ℕ × ℕ) _
(a₁ , _) ≈ (a₂ , _) = a₁ ≡ a₂
≈-sym : Symmetric _≈_
≈-sym refl = refl
≋-sym : Symmetric (ListRel _≈_)
≋-sym = symmetric ≈-sym
In the last line Agda complains that it can't solve for the second projections out of the pairs. Interestingly changing the last line to the following eta-equivalent expression means that it can solve them:
≋-sym = symmetric (λ {x} {y} → ≈-sym {x} {y})
Now naturally I know that sometimes Agda can't solve for all the implicit arguments and needs a bit of extra help but I don't understand what new information I'm providing it by doing this...
I'm doing a lot of lifting of equality and I'd rather avoid adding these ugly eta expansions everywhere in my code. I was wondering if anyone has any suggestions to allow something similar to the original code to pass?
I have looked into irrelevance but the second projection is used elsewhere, even if it is computationally irrelevant for equality.
I'm guessing, but I think the problem is that the order of implicit arguments doesn't matter for (a part of) unification. Consider
flipped : (({n m : ℕ} -> n ≡ m) -> ℕ) -> ({m n : ℕ} -> n ≡ m) -> ℕ
flipped k f = k f
Here k receives something of type {n m : ℕ} -> n ≡ m and f is of type {m n : ℕ} -> n ≡ m (m comes before n), but Agda happily unifies these two expressions, since each implicit argument becomes a metavariable during elaboration and metas are not ordered by when they was introduced — they are ordered by how they was instantiated (e.g. you can't instantiate α to β -> β and then β to α as it would result in α ≡ α -> α and handling such loops (called equirecursive types) is an unsound nightmare). So when you write
≋-sym = symmetric ≈-sym
Agda is confused, because it could mean any of
≋-sym = symmetric (λ {x} {y} → ≈-sym {x} {y})
≋-sym = symmetric (λ {x} {y} → ≈-sym {y} {x})
(well, not quite, because the second expression is ill-typed, but Agda doesn't backtrack and can't solve such problems therefore)
This is different from the flipped example, because flipped explicitly specifies how n and m are used, so unification of
{n1 m1 : ℕ} -> n1 ≡ m1
{m2 n2 : ℕ} -> n2 ≡ m2
results in n1 ≡ n2 and m1 ≡ m2 and hence the problem is solved. If you drop this specification
unsolved : (({n m : ℕ} -> ℕ) -> ℕ) -> ({m n : ℕ} -> ℕ) -> ℕ
unsolved k f = k f
you'll get unsolved metas back.
The exact problem with your definition is that only the first projections of pairs are mentioned in the RHS of _≈_, so Agda doesn't know how to unify the second projections. Here is a workaround:
record Sing {α} {A : Set α} (x : A) : Set where
module Test where
_≈_ : Rel (ℕ × ℕ) _
(a₁ , b₁) ≈ (a₂ , b₂) = a₁ ≡ a₂ × Sing (b₁ , b₂)
≈-sym : Symmetric _≈_
≈-sym (refl , _) = (refl , _)
≋-sym : Symmetric (ListRel _≈_)
≋-sym = symmetric ≈-sym
Sing is a dummy record that have only one inhabitant that can be inferred automatically. But Sing allows to mention b₁ and b₂ in the RHS of _≈_ in an inference-friendly way, which makes those metas in ≋-sym solvable.
Though, it seems that (a₁ , b₁) ≈ (a₂ , b₂) = a₁ ≡ a₂ × Sing b₁ already gives Agda enough hints how to solve metas in ≋-sym.
You can also define a pattern synonym to make the code slightly nicer:
pattern refl₁ = (refl , _)
≈-sym : Symmetric _≈_
≈-sym refl₁ = refl₁

Defining Functor Instance for Tensor Type (Idris)

I've been learning Idris recently, and decided I'd try to write a simple tensor library. I started by defining the type.
data Tensor : Vect n Nat -> Type -> Type
Scalar : a -> Tensor [] a
Dimension : Vect n (Tensor d a) -> Tensor (n::d) a
As you can see, the type Tensor is parameterized by a Vect of Nats describing the tensor's dimensions, and a type, describing its contents. So far so good. Next I decided to try making the Tensor type a Functor.
instance Functor (Tensor d) where
map f (Scalar x) = f x
map f (Dimension x) = map f x
And Idris gave me the following error.
Unifying `b` and `Tensor [] b` would lead to infinite type
Okay. From the error, I figured that maybe the issue was that the first pattern of map was too specific (i.e., would only accept scalars when the type declaration of map is such that it accepts any tensor). That seemed odd, but I figured I'd try rewriting it using a with statement.
dimensions : {d : Vect n Nat} -> Tensor d a -> Vect n Nat
dimensions {d} _ = d
instance Functor (Tensor d) where
map f t with (dimensions t)
map f (Scalar x) | [] = f x
map f (Dimension x) | (_::_) = map f x
But I got the same error. I have quite a bit of experience in Haskell, but I'm still not quite used to the lingo used in dependently typed programming in general and by Idris in particular, so any help understanding the error message would be greatly appreciated.
(Note: from Idris 0.10, the instance keyword is deprecated and should be simply left out).
The task is to apply the function to all elements in the Scalar constructors, but otherwise leave the structure unchanged. So, we need to map Scalar to Scalar and Dimension to Dimension, and since Dimension contains a vector of recursive occurrences, we should use Vect's map to access them.
Functor (Tensor d) where
map f (Scalar x) = Scalar (f x)
map f (Dimension xs) = Dimension (map (map f) xs)
So, in map (map f) xs, the first map is for mapping over Vect, and map f is the recursive call.

Is there a modulo function in idris?

In Haskell, there are the mod and rem functions. Are there similar functions in Idris, particularly defined over Nat?
In Prelude.Nat there are
modNatNZ : Nat -> (y : Nat) -> Not (y = 0) -> Nat
modNat : Nat -> Nat -> Nat
The first one needs a proof that the divisor is not zero, while the second is partial (i. e. can crash during runtime). Practically there is also a proof,
SIsNotZ : {x: Nat} -> Not (S x = Z)
that a successor cannot be zero. So you can just use modNatNZ 10 3 SIsNotZ and the unification system will proof Not (3 = 0). You can see how modNatNZ works here. As Nat is always positive, a remainder function would behave the same.
Otherwise, a generic
mod : Integral ty => ty -> ty -> ty
is defined for all types implementing Integral (e.g. Int).

Common Lisp best practices to use type declarations for optimization

I have a Common Lisp function that merges two ordered lists of symbols, without duplicates (two ordered sets):
(defun my-merge (x y)
"merge two lists of symbols *already sorted and without duplicates*
(and return the resulting list sorted and without duplicates)"
(let* ((first (cons nil nil))
(last first))
(loop while (and x y)
for cx = (car x)
for cy = (car y)
if (string= cx cy)
do (setf x (cdr x))
else if (string< cx cy)
do (rplacd last (cons cx nil))
and do (setf last (cdr last)
x (cdr x))
else do (rplacd last (cons cy nil))
and do (setf last (cdr last)
y (cdr y)))
(rplacd last (or x y))
(cdr first)))
Since I have found only scarce information about the use of type declarations in practical cases in order to compile efficiently the code, I am unsure if it is sufficient to declare the variables, for instance in this way:
(defun my-merge (x y)
"merge two list of symbols *already sorted and without duplicates*"
(declare (list x y))
(let* ((first (cons nil nil))
(last first))
(declare (cons first last))
(loop while (and x y)
for cx symbol = (car x)
for cy symbol = (car y)
...
or, as I suppose, if it is necessary also to add the the specifier to my code? But then, where and in which cases should I add it?
There is some rule that one can follow?
Should I also declare the type of my functions, again for the optimization purposes?
Style
Since you don't actually use the extended LOOP features in any useful way and the LOOP syntax isn't that great for your example, I would propose to write it with the primitive LOOP. See how COND makes it more readable for a Lisp programmer:
(defun my-merge (x y &aux (first (list nil)) (last first) cx cy)
(macrolet ((cdr! (v)
`(setf ,v (cdr ,v))))
(loop (unless (and x y)
(return))
(setf cx (car x) cy (car y))
(cond ((string= cx cy)
(cdr! x))
((string< cx cy)
(rplacd last (list cx))
(cdr! last)
(cdr! x))
(t
(rplacd last (list cy))
(cdr! last)
(cdr! y))))
(rplacd last (or x y))
(cdr first)))
Compiling
Given the level of sophistication of a compiler:
fully stupid = compiler ignores all declarations -> declarations don't help
mostly stupid = compiler needs declarations everywhere, but optimizes -> you need to write a lot of declarations
example:
(let ((a 1) (b 2))
(declare (integer a b))
(let ((c (the integer (* (the integer (+ a b))
(the integer (- a b))))))
(declare (integer c))
(the integer (* c c))))
Note that it might not enough to know what the argument types are, it might be necessary to declare the type of results. Thus the use of the. DISASSEMBLE and the profiler are your friends.
basic = compiler needs type declarations, optimizes, but also can infer some types. Types for the standard language is known.
Even better compilers complain about type errors, can propagate types across functions and can complain when certain optimizations are not possible.
Sequence functions
Note that sequence functions are a particular tough case. Sequences have as subtypes lists and vectors (including strings).
Let's say a sequence function is:
(foo result-type sequence-1 sequence-2 fn)
if the sequences are of the same type, one might want to have an optimized code versions for lists and another one for vectors.
if the sequences are of different types, it might be useful to convert one sequences to a different type. Maybe not.
the result type also has influence, depending on result types, different algorithms may be possible/necessary
So the degree of freedom is quite high. The compiler might contribute to fast code. But also the implementation of the particular sequence function might be able to do some optimization at runtime.
Then fn is a function which takes elements and produces new elements. It might be helpful to know its type signature - or not.
I can't really say which current Common Lisp has a sophisticated implementation of the sequence functions. Though I remember that the Symbolics Common Lisp implementations put some effort into it.
Documentation and papers
Often what the compiler can optimize and how is not well documented, if at all. There are some papers about this topic, but often they are old and/or outdated.
The Python compiler of CMUCL: The Compiler.
The Python compiler of CMUCL: Advanced Compiler Use.
The Python compiler for CMU Common Lisp (Postscript)
SBCL Compiler
Allegro CL: Compiling
LispWorks: Optimizing your code
Performance beyond expectations
How to make Lisp code go faster than C
An evaluation of major Lisp compilers

Apply list of values as arguments to a function

Can I apply a list of n values to a function that takes n values, where n varies?
A first naive attempt is the following but the compiler (fairly) complains of a weird self-referential type for applyN
applyN f xs =
case xs of
[] -> f
(x::xs) -> applyN (f x) xs
I can't see how a fold would work and respect its type signature either.
For context, I want to take a list of N Json Decoders and evaluate
Json.objectN ConstructorN n1 n2 ... nN
Clearly if n is known (let's say 2) then we have
case lst of
(d1 :: d2 :: _) -> Json.object2 Constructor2 d1 d2
otherwise -> ....
but that's a lot of code to write if I cannot generalise for n.
I'm fearing it is not possible as in Haskell it needs some special compiler flags.
No, you can not do that, at least not without dependant types or at least some type level trickery, which there is none of in Elm (for reference: How do I define Lisp’s apply in Haskell?)
(This is why there are all the objectN functions by the way.)
Try to restructure your code - can't f just take a list?
In this context of Json decoding, if you have a list literal with decoders, you can do something equivalent to applyN. This pattern uses the functions map: (a -> b) -> Decoder a -> Decoder b and andMap : Decoder (a -> b) -> Decoder a -> Decoder b. You use it like so:
Constructor
`Json.map` n1
`Json.andMap` n2
`Json.andMap` ...
`Json.andMap` nN
Sadly andMap isn't offered for every core module. You can define it yourself if there is a map2 or andThen. In this case object2 works, it's basically the same as map2. So:
andMap : Decoder (a -> b) -> Decoder a -> Decoder b
andMap decFun decA =
object2 (\f a -> f a) decFun decA
You can also use Json.Decode.Extra.apply, which is the same thing, just named in a non-standard way*.
*non-standard within the Elm world anyway