In Idris2, can't typecheck if then else directly - typechecking

I'm a beginner with Idris. In Idris2 version 0.3.0, I observed a strange behavior.
Why doesn't this code type check ?
f : Type -> Type -> Type
f a b = (c : Bool) -> if c then a else b
While processing right hand side of f. Main.case block in f is not accessible in this context.
While this code type checks without issues:
f1 : Type -> Type -> Bool -> Type
f1 a b c = if c then a else b
f' : Type -> Type -> Type
f' a b = (c : Bool) -> f1 a b c
Is this a bug?
Initially, I was trying to prove a stupid theorem which should be obvious, but Refl doesn't work. It looks like it's unable to understand that the variables match.
module Main
import Data.Nat
t : Nat -> Nat -> Nat
t l r = if lte l r then l else r
prop : Nat -> Nat -> Type
prop a b = (t a b = if lte a b then a else b)
proof_prop : prop a b
proof_prop = ?imlost

Your if_then_else_ using program starts failing in commit 04a0f5001f:
Correct multiplicities when checking Pi binders
We've always just used 0, which isn't correct if the function is going
to be used in a runtime pattern match. Now calculate correctly so that
we're explicit about which type level variables are used at runtime.
This might cause some programs to fail to compile, if they use
functions that calculate Pi types. The solution is to make those
functions explicitly 0 multiplicity. If that doesn't work, you may
have been accidentally trying to use compile-time only data at run
time!
Fixes #1163
The solution is to mark your f function as type-level-only:
0 f : Type -> Type -> Type
f a b = (c : Bool) -> if c then a else b

Related

Why does Idris 2 fail to resolve constraints with function composition in this trivial example?

I have encountered a problem with some code I am trying to write in Idris 2. I would like to resolve this issue, but more importantly, I wish to understand it more deeply and develop some skills in diagnosing such issues in general.
I have distilled the problem to the following relatively trivial example:
data D : Nat -> Type where
V : (n : Nat) -> D n
d : (n : Nat) -> D n
d n = V n
f : D n -> String
f (V n) = show n
t : Nat -> String
t = f . d
The definition of t fails type checking with the following output:
Error: While processing right hand side of t. Can't solve constraint between: ?n [no locals in scope] and n.
Test:11:9--11:10
07 | f : D n -> String
08 | f (V n) = show n
09 |
10 | t : Nat -> String
11 | t = f . d
Some experimentation has revealed that the following alternative definitions for t also fail type checking:
t : Nat -> String
t n = (f . d) n
t : Nat -> String
t = \n => (f . d) n
While these alternatives type check successfully:
t : Nat -> String
t n = f (d n)
t : Nat -> String
t = \n => f (d n)
I am endeavouring to learn Idris (for the second time), and so while I could move on with the definitions which don't involve function composition, I would like to improve my understanding.
It seems to me that the definitions which pass type checking are simply syntactic alternatives with identical semantics and behaviour, and I don't understand why the function composition definitions fail type checking. I would also like to understand the particular error message the type checker reports, so that I can deepen my understanding and resolve similar type checking errors in the future.
I have a few broad questions:
How should I interpret the error reported by the type checker in this example, and how can I gather more information about the ?n and n types mentioned? I particularly welcome any advice or tips on how to go about understanding and resolving such an error (teach a man to fish, as the saying goes).
Why do the definitions involving function composition fail type checking?
What is the best solution for this example? Should I just use a definition which does not involve function composition? Is there a better alternative?
Let look at the types involved
Prelude.. : (b -> c ) -> (a -> b ) -> a -> c
f : D n -> String
d : (n : Nat) -> D n
The problem is:
(a -> b )
(n : Nat) -> D n
cannot be unified because (a -> b) does not allow the value of the argument to determine the type of return value.

Order of implicit arguments in idris

How does the order of implicit arguments affect idris?
I just read this post and I got curious about MkPair's type signature.
I tried MkPair 10 on REPL and I got
(input):Can't infer argument B to Builtins.MkPair
And this is exactly what I expected. From its type signature Builtins.MkPair : {A : Type} -> {B : Type} -> (a : A) -> (b : B) -> (A, B), I have to pass B(whether it's implicit or explicit) before apply a value for a.
At the same time, I expected q 10 to work, cause its type is q : {A : Type} -> (a : A) -> {B : Type} -> (b : B) -> (A, B) which tells me that I don't need any value for B before I apply a value for A and a.
But it also failed with the same message!
(input):Can't infer argument B to Main.q
What happens to q?
And I have one more question . Before I found that q worked neither, I was gonna ask the reason that Idris compiler prefers MkPair's signature to q's one. MkPair looks unnecessarily eager to me. Why does it demand B too early?
Without any context to guide the compiler, I guess Idris stays true to its eager by default attitude and tries to infer all the implicit arguments that appear before the last "explicit" one specified, before doing automatic partial application.
Note that you can suppress this behavior by placing the expression in an appropriately typed hole:
the ({b : Type} -> b -> (Nat, b)) (q 10)
-- or
r : {b : Type} -> b -> (Nat, b)
r = q 10

Notational syntax sugar for list and do notation

So I've noticed that in Idris if you define your own list or vector like type — for example the following type I've found to be useful:
data HFVec : (f : Type -> Type) -> (n : Nat) -> Vec n Type -> Type where
Nil : HFVec f Z []
(::) : (a : f t) -> HFVec f n ts -> HFVec f (S n) (t :: ts)
— then you get list syntax for free:
test : HFVec List 2 [Int, String]
test = [[3], [""]]
I assume this is done when you have a constructor named ::, but I don't know for certain. In the same way you get do-notation if you have a constructor named >>= even if there is no monad implementation:
data Test : Type -> Type where
Pure : a -> Test a
(>>=) : Test a -> (a -> Test b) -> Test b
test : Test Int
test = do
Pure 1
x <- Pure 2
Pure x
This is a pretty cool feature, the only thing is I have not found it documented anywhere. It would be good to know exactly how these mechanisms work so one can know under exactly what circumstances they can be expected to work. Also, are these kind of rules the privilege of the compiler, or can the user make them with the syntax and dsl features?

OCaml Understanding Functions and Partial Applications

I am writing a form of form of transform in OCaml that takes in a function and also accepts a list to transform. I understand something is wrong with my pattern matching in terms of type-checking, as it will not compile and claims the types do not match but I am not sure what exactly is wrong with my cases.
I receive an actual declaration error underlining the name of the function when I attempt to compile.
let rec convert (fun: 'b -> 'c option) (l: 'b list) : 'c list =
begin match l with
| [] -> []
| h::tl -> if f h = Some h then h :: convert f tl
else convert f tl
end
I wrote the following test, which should pass in order to ensure the function works properly.
let test () : bool =
let f = func x -> if x > 3 then Some (x + 1) else None in
convert f [-1; 3; 4] = [5]
;; run_test "Add one" test
I am pretty confident the error is somewhere in my second pattern match.
You should provide the exact error message in the future when asking about a compilation error (as well as the position the compiler complains about).
In h :: convert f tl, convert f tl is 'c list, but h is 'b, so you can't combine them like this. Neither does f h = Some h make sense: f h is 'c option and Some h is 'b option. You probably want to match f h instead:
| h::tl -> match f h with
| Some h1 -> ...
| None -> ...

OCaml: Is there a function with type 'a -> 'a other than the identity function?

This isn't a homework question, by the way. It got brought up in class but my teacher couldn't think of any. Thanks.
How do you define the identity functions ? If you're only considering the syntax, there are different identity functions, which all have the correct type:
let f x = x
let f2 x = (fun y -> y) x
let f3 x = (fun y -> y) (fun y -> y) x
let f4 x = (fun y -> (fun y -> y) y) x
let f5 x = (fun y z -> z) x x
let f6 x = if false then x else x
There are even weirder functions:
let f7 x = if Random.bool() then x else x
let f8 x = if Sys.argv < 5 then x else x
If you restrict yourself to a pure subset of OCaml (which rules out f7 and f8), all the functions you can build verify an observational equation that ensures, in a sense, that what they compute is the identity : for all value f : 'a -> 'a, we have that f x = x
This equation does not depend on the specific function, it is uniquely determined by the type. There are several theorems (framed in different contexts) that formalize the informal idea that "a polymorphic function can't change a parameter of polymorphic type, only pass it around". See for example the paper of Philip Wadler, Theorems for free!.
The nice thing with those theorems is that they don't only apply to the 'a -> 'a case, which is not so interesting. You can get a theorem out of the ('a -> 'a -> bool) -> 'a list -> 'a list type of a sorting function, which says that its application commutes with the mapping of a monotonous function.
More formally, if you have any function s with such a type, then for all types u, v, functions cmp_u : u -> u -> bool, cmp_v : v -> v -> bool, f : u -> v, and list li : u list, and if cmp_u u u' implies cmp_v (f u) (f u') (f is monotonous), you have :
map f (s cmp_u li) = s cmp_v (map f li)
This is indeed true when s is exactly a sorting function, but I find it impressive to be able to prove that it is true of any function s with the same type.
Once you allow non-termination, either by diverging (looping indefinitely, as with the let rec f x = f x function given above), or by raising exceptions, of course you can have anything : you can build a function of type 'a -> 'b, and types don't mean anything anymore. Using Obj.magic : 'a -> 'b has the same effect.
There are saner ways to lose the equivalence to identity : you could work inside a non-empty environment, with predefined values accessible from the function. Consider for example the following function :
let counter = ref 0
let f x = incr counter; x
You still that the property that for all x, f x = x : if you only consider the return value, your function still behaves as the identity. But once you consider side-effects, you're not equivalent to the (side-effect-free) identity anymore : if I know counter, I can write a separating function that returns true when given this function f, and would return false for pure identity functions.
let separate g =
let before = !counter in
g ();
!counter = before + 1
If counter is hidden (for example by a module signature, or simply let f = let counter = ... in fun x -> ...), and no other function can observe it, then we again can't distinguish f and the pure identity functions. So the story is much more subtle in presence of local state.
let rec f x = f (f x)
This function never terminates, but it does have type 'a -> 'a.
If we only allow total functions, the question becomes more interesting. Without using evil tricks, it's not possible to write a total function of type 'a -> 'a, but evil tricks are fun so:
let f (x:'a):'a = Obj.magic 42
Obj.magic is an evil abomination of type 'a -> 'b which allows all kinds of shenanigans to circumvent the type system.
On second thought that one isn't total either because it will crash when used with boxed types.
So the real answer is: the identity function is the only total function of type 'a -> 'a.
Throwing an exception can also give you an 'a -> 'a type:
# let f (x:'a) : 'a = raise (Failure "aaa");;
val f : 'a -> 'a = <fun>
If you restrict yourself to a "reasonable" strongly normalizing typed λ-calculus, there is a single function of type ∀α α→α, which is the identity function. You can prove it by examining the possible normal forms of a term of this type.
Philip Wadler's 1989 article "Theorems for Free" explains how functions having polymorphic types necessarily satisfy certain theorems (e.g. a map-like function commutes with composition).
There are however some nonintuitive issues when one deals with much polymorphism. For instance, there is a standard trick for encoding inductive types and recursion with impredicative polymorphism, by representing an inductive object (e.g. a list) using its recursor function. In some cases, there are terms belonging to the type of the recursor function that are not recursor functions; there is an example in §4.3.1 of Christine Paulin's PhD thesis.

Resources