7

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?

frosch03
  • 719
  • 4
  • 13

2 Answers2

9

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.

dflemstr
  • 25,947
  • 5
  • 70
  • 105
  • 1
    How irritating. One would expect the type system to be able to conclude that `snd aTuple10` has type `String`; could this be considered a bug in the implementation? Surely Haskell 2010 doesn't specify such behavior. One might argue that if the implementation doesn't know what type the first thing has, it won't know where in memory the second thing is, but since we are dealing with boxed tuples here, there should always be two pointers, and therefore the second element is easily locatable regardless of the type of the first. – Dan Burton Feb 27 '12 at 21:41
  • 2
    A function like `class Boolish a where toBool :: a -> Bool; foo :: Boolish a => (a, b) -> b; foo (a, b) = if toBool a then b else undefined` is conceivable, so the result of the function can depend on an ambiguous parameter. Special-casing for `snd` would be strange in that situation. – dflemstr Feb 27 '12 at 21:54
  • You saved my day, the `(23 :: Int)` trick makes the magic! Thanks – dani24 Feb 15 '17 at 23:08
-1

I tried to compile this:

import Control.Arrow

funTimes10 = (*10)
describe10 = "times 10"

tuple10 :: (Num b) => ((b -> b), String)
tuple10 = (,) funTimes10 describe10

aTuple10 :: (Arrow a, Num b) => (a b b, String)
aTuple10 = (,) (arr funTimes10) describe10

But I get this:

Could not deduce (b ~ Integer)
from the context (Arrow a, Num b)
  bound by the type signature for
             aTuple10 :: (Arrow a, Num b) => (a b b, String)
  at D:\dev\haskell\arr_tuple.hs:10:1-42
  `b' is a rigid type variable bound by
      the type signature for
        aTuple10 :: (Arrow a, Num b) => (a b b, String)
      at D:\dev\haskell\arr_tuple.hs:10:1
Expected type: b -> b
  Actual type: Integer -> Integer
In the first argument of `arr', namely `funTimes10'
In the first argument of `(,)', namely `(arr funTimes10)'

So, my guess is that you need to decide which arrow instance you want to use. I.e. you might need to specify the concrete type of arr funTimes with an annotation.

Andre
  • 1,577
  • 1
  • 13
  • 25
  • The error here is completely unrelated to the problem at hand. It appears because you forgot a type signature for `funTimes10`. Check out the [Monomorphism Restriction](http://www.haskell.org/haskellwiki/Monomorphism_restriction). – dflemstr Feb 27 '12 at 14:06
  • @dflemstr does my answer show a problem with the original question? **I** did not forget a type signature, it was not there in the original post. Although I did not describe the underlying problem (because I did not see it), I don't think it's worth downvoting my answer. – Andre Feb 27 '12 at 16:00
  • 1
    I think the OP just copied the definition of his functions to demonstrate which kinds of values he's using. He has a polymorphic `funTimes10`, or he would have gotten a different error. – dflemstr Feb 27 '12 at 16:44