In the agda docs, I read that when "some meta-variable other than the goals cannot be solved the code will be highlighted in yellow"
I'm trying to understand this in a somewhat degenerate case.
If I define a regular product type then a stupid program works fine.
data _==_ {l}{X : Set l}(x : X) : X -> Set where
refl : x == x
data prod (A B : Set) : Set where
_,,_ : A → B → prod A B
fst' : {A B : Set} → prod A B → A
fst' (x ,, x₁) = x
stupid : fst' (3 ,, 3) == 3
stupid = refl
However, if I use a product as a special case of a dependent product, I get the yellow highlighting for stupid''''. Specifically, the fst and and the second 3 are highlighed yellow. Why do all the other stupid*'s work except for stupid''''? Are there any general tips for debugging yellow highlighting errors in agda?
record Sg {l}(S : Set l)(T : S -> Set l) : Set l where
constructor _,_
field
fst : S
snd : T fst
open Sg public
_*_ : forall {l} -> Set l -> Set l -> Set l
S * T = Sg S \ _ -> T
infixr 40 _,_
infixr 20 _*_
threethree : Nat * Nat
threethree = 3 , 3
three : Nat
three = fst threethree
stupid'' : three == 3
stupid'' = refl
stupid''' : fst (threethree) == 3
stupid''' = refl
--here's the yellow highlighting
stupid'''' : fst (3 , 3) == 3
stupid'''' = refl
--here's the yellow highlighting
stupid'''' : fst (3 , 3) == 3
stupid'''' = refl
This is because Agda can't infer the type of (3 , 3) to supply it to fst.
"But that's just Nat * Nat!"
Not necessarily, it can be
Sg Nat \n -> if n == 3 then Nat else Bool
or any other weird type that gives Nat as a type of the second element whenever the first element is 3 and does something completely different in all other cases.
And Agda's unification machinery always either finds a unique solution or gives up.
You've asked Agda to solve the following unification problem:
_T 3 =?= Nat
and clearly there are way too many different _Ts that return Nat when the argument is 3.
Why do all the other stupid*'s work except for stupid''''
Because in all the other ones there's no ambiguity:
in stupid the type of the second element does not depend on the first element (due to the definition of prod)
in other cases you explicitly specify the type of the argument (via a standalone declaration)
Related
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.
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
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
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?
So here is my goofy sandbox to play with Applicatives in PureScript
module Main where
import Debug.Trace
data Foo a
= Foo a
instance showFoo :: (Show a) => Show (Foo a) where
show (Foo a) = "I pity da (Foo " ++ (show a) ++ ")"
instance functorFoo :: Functor Foo where
(<$>) f (Foo a) = Foo (f a)
instance applyFoo :: Apply Foo where
(<*>) (Foo a) (Foo b) = Foo (a b)
m :: Number -> Number -> Number -> Number
m x y z = x * y - z
main = trace <<< show $ m <$> Foo 14
<*> Foo 2
<*> Foo 5
The above works fine, but if I remove:
m :: Number -> Number -> Number -> Number
it does not compile
Error at pure.purs line 18, column 1:
Error in declaration m
No instance found for Prelude.Num u1150
However (+) and (-) are both of type
forall a. (Prelude.Num a) => a -> a -> a
Why can't Number be inferred?
The reality is that when learning PureScript and coming from a dynamic language (JavaScript), I run into type errors frequently. Developing skills in diagnosing and understanding these errors is challenging without a grasp of when inference can occur and when it can't. Otherwise I will have to write types every single time in order to feel confident in my code (lameness).
This is because at the moment the compiler can't infer typeclass constraints, and as you noted the arithmetic operators are all defined in the Num typeclass.
The type that would be inferred for m (if the compiler could) would be something like:
m :: forall a. (Num a) => a -> a -> a -> a
On your second point typing top level declarations is considered good style anyway, as it helps to document your code: see here for a fuller explanation.