tags:

views:

134

answers:

2

I have a problem with Haskell's scoping in where definitions. When I have the following function f, where I want to pass the x to the locally defined function f1 without explicitely using it as a parameter, I get an error saying that the type of x is incompatible with the one in the output of f1, although it should be the same:

f :: Eq a => a -> [a]
f x = f1 x
    where
        f1 :: Eq a => a -> [a]
        f1 y = [ x, y ]

The error is the following:

    Couldn't match expected type `a1' against inferred type `a'
      `a1' is a rigid type variable bound by
           the type signature for `f1' at test.hs:4:11
      `a' is a rigid type variable bound by
          the type signature for `f' at test.hs:1:8
    In the expression: x
    In the expression: [x, y]
    In the definition of `f1': f1 y = [x, y]
Failed, modules loaded: none.

When I however pass the x as an additional parameter, as I did in the following code with the function g, it works fine:

g :: Eq a => a -> [a]
g x = g1 x x
    where
        g1 :: Eq a => a -> a -> [a]
        g1 x y = [ x, y ]

Is there a way to make the type a in f compatible to the type a (or a1) in f1?

+6  A: 

The problem with your code is the locally scoped f1 type signature. It specifies that f1 can take any type

f1 :: Eq a => a -> [a]

Even though this is a local function, you've generalized this function to be able to take a type that won't exist within f, whatever this function receives HAS to come from f, so the type signature is unnecessary.

Just remove the f1 type signature.

Edit: Read my post back to myself, it's a bit unclear. a in f1 is a parameterized type that can take anything, but the arguments passed to it are already bound in f. So this function can only receive what its parent function receives, the type signature you're giving it breaks that rule. Hope that's a little more clear.

Dave
Yeah, that works, thanks :) Although it still leaves a misgiving not being able to explicitely type the `f1` function (I'm coming from far stricter language), but well, as long as nothing breaks ;)
poke
Poke: The problem here is that Haskell is being more type strict than you are expecting it to be. Your original type signature for 'f1' specified that the argument could be anything belonging to the 'Eq' typeclass, but the compiler correctly saw that the argument passed to 'f1' actually had to be exactly what was passed to 'f'. Removing the type signature for 'f1' does not loosen type strictness. Doing so only allows the compiler to infer typing for you.
Michael Steele
+7  A: 

Dave is right above. Another way to think of it is that even though both of your type signatures refer to the variable a, it's not actually the same type variable. In the Haskell-prime notation, both signatures can be more explicitly written as:

forall a . Eq a => a -> [a]

meaning that for both functions, they can accept an argument of any type whatsoever (within Eq). This is obviously not the case here. In standard Haskell 98, the only option is to forgo the type signature for f1. But GHC (and others?) support lexically scoped type variables. So you could write

f :: forall a. Eq a => a -> [a]
f x = f1 x
    where
        f1 :: a -> [a]
        f1 y = [ x, y ]

and that would work fine.

Dan
Oh I really like that solution! Although it's not a good option for my actual problem now, I will make sure to make use of this in future :) Thank you very much!
poke
Why not? What's your actual problem?
Dan
It was related to homework for uni, and I doubt we should use compiler specific features there (especially as I don't know what compiler they will be testing it with) :)
poke