Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Arrows inside Tuples

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, but
  • i don't get any description string with snd.

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?

like image 657
frosch03 Avatar asked Feb 27 '12 13:02

frosch03


1 Answers

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.

like image 123
dflemstr Avatar answered Sep 26 '22 12:09

dflemstr