I want to crate a tuple, that holds an arrow and a string that describes the arrow. If i do so with functions (instead of arrows), the following works like expected:
funTimes10 = (*10)
describe10 = "times 10"
tuple10 :: (Num b) => ((b -> b), String)
tuple10 = (,) funTimes10 describe10
I can access the function with fst
, and with snd
i get the description string of the function.
However, if i exchange the function with an arrow, like in the following:
aTuple10 :: (Arrow a, Num b) => (a b b, String)
aTuple10 = (,) (arr funTimes10) describe10
fst
still works and returns my arrow, butsnd
. I only got this error-message:
Ambiguous type variable `a0' in the constraint:
(Arrow a0) arising from a use of `aTuple10'
Probable fix: add a type signature that fixes these type variable(s)
In the first argument of `snd', namely `aTuple10'
In the expression: (snd aTuple10)
In an equation for `it': it = (snd aTuple10)
Why do i get this error, and what should i do, to avoid it?
Let's look at the type of snd
:
snd :: (foo, x) -> x
(I renamed the type variables for clarity)
What the type states is that for a tuple with types foo
and x
, return something of type x
. Something important to know here is that while the value system aka. runtime in Haskell is lazy, Haskell's type system is strict, meaning that both the types of foo
and x
must be known before snd
can be called.
In the first case, when you just have a Num b => (b -> b, String)
, calling snd
will leave b
ambiguous, because you don't mention its concrete type anywhere, and it can't be inferred from the return type because foo ~ b
which is distinct from x
. In other words: because (b, b)
can be a tuple of any number type, and the type checker can't figure out which one, it is ambiguous. The trick here is that we'll have Haskell's defaulting rules kick in, which state that if a numeric type is ambiguous, it should default to Integer
. If you had turned warnings on with -Wall
, it would have said that this is happening. So, our type becomes (Integer -> Integer, String)
and snd
can be called.
In the second case, however, we still manage to infer b
via the defaulting rules, but there is no default Arrow
for a
, so we're stuck! You must explicitly specify which arrow you want in order to continue! You can either do this by first using a value of aTuple10
somewhere else:
let bla = aTuple10 -- We do this because `aTuple10` can have type variables, but `bla` cannot (by default)
fst bla (23 :: Int) -- This fixes the type of `bla`, so that `a ~ (->)` and `b ~ Int`
print $ snd bla -- So the arrow isn't ambiguous here
... or you can just specify the type that you want:
print $ snd (aTuple10 :: (Int -> Int, String))
PS if you want to change the default type of ambiguous numbers, the default
keyword can help you out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With