7

I am pretty sure this has been asked before, however I was unable to find the right answer:

I tried to eliminate the ambiguity in the following exemplary code snippet:

{-# LANGUAGE MultiParamTypeClasses #-}

class FooBar a b where
    foo :: a -> a
    foo = id
    bar :: a -> a
    bar = foo        -- ERROR AT THIS LINE

I get an error message like so:

Ambiguous type variable `b0' in the constraint:
      (FooBar a b0) arising from a use of `foo'
    Probable fix: add a type signature that fixes these type variable(s)
    In the expression: foo
    In an equation for `bar': bar = foo

which is understandable. Note however, that I'm actually unable to follow the compiler's advice and fix the type variable in question: foo doesn't contain b type variable in its type signature. Also this doesn't work:

    bar = (foo :: FooBar a b => a -> a) -- ERROR, the same error message

Not even with -XScopedTypeVariables enabled.

How to tell GHC which FooBar to use? Is it even possible?

EDIT

Following some answers: yes, I've heard about both functional dependencies and associated types.

As to functional dependencies - I am looking for a way to explicitly inform GHC which instance to use (as opposed to asking it to derive the correct type all by itself).

As to both - in the real world example that this one comes from, I actually need both of the parameters a and b to be independent.

Again: I know this example is extremely silly, I know b is not used in the body of the type class, which makes no sense. This is an extreme simplification of a real world example, that actually does make sense. The real use-case involves "substitutability" of types a using types b and then "unifiability" of types a and b using types c and unfortunately is much longer.

EDIT(2)

Okay sorry. I think you convinced me after all that I have to refactor the code in order not to have any ill-defined functions (i.e. ones that don't contain all the independent types in their type signatures). Talking to you really helped me think this thing though. Appreciated.

julx
  • 8,694
  • 6
  • 47
  • 86
  • Can you explain why would you like to do that? `FooBar`'s implementation only relies on `a`, and does not depend on `b`, so what's the point of mentioning `b`, at all? – lbolla Dec 07 '11 at 14:02
  • @lbolla Yes, I could explain that, however that would take twice as long as all the code snippets here and would involve not one but 3 different type classes. I am afraid you'll have to trust me on that one. It's just an example that could be extended with additional methods that would require both `a` and `b`. – julx Dec 07 '11 at 14:05
  • @julkiewicz: I've edited my answer to answer your EDIT. – jmg Dec 07 '11 at 14:28

2 Answers2

8

Your class is ill-defined even if you remove bar completely from the class. Let's just assume you have the following class:

class FooBar a b
  where
    foo :: a -> a
    foo = id

Suppose now you would like to use foo outside the definition of this class.

e = .... foo x .....

GHC will complain with a similar error message than yours, that it can't find a type for b. Simply because your code doesn't supply such a type. Your type class declaration says every pair of types a and b might get an instance. So, even when a is fixed in a call of foo there are still arbitrary many possible instances to select from.

A functional dependency a -> b would solve that problem, because it states: Given a type a there may be only one instance of Foobar a b. An associated type avoids the problem by not using multi-parameter type classes.

regarding the OP's EDIT:

If you insist on having two type parameters. Then every method's signature of the type class has to contain every type variable of the class. There is no way around it.

jmg
  • 7,308
  • 1
  • 18
  • 22
6

If you introduce a functional dependency, class FooBar a b | a -> b where, that would settle it, but without that (or associated types), I think it is impossible to resolve the instance to use.

Oh, one way, but kind of ugly: introduce a dummy parameter of type b

class FooBar a b where
    foo' :: b -> a -> a
    foo' _ = id
    bar' :: b -> a -> a
    bar' b = foo b
Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • Yes, I've read about both functional dependencies and associated types - both could help. The problem is that really what GHC lacks in this case is not some elaborate type system feature that would derive the correct type all by itself. It just lacks the syntax to inform it explicitly which types it should use. At least that's how I see it. – julx Dec 07 '11 at 14:08
  • 4
    One hack I've seen that's slightly less ugly than adding a dummy parameter of type `b` is to have `data Proxy a = Proxy` and then add a dummy parameter of type `Proxy b`. It avoids the messy "well, `undefined` is okay because the function never looks at it" reasoning. – Daniel Wagner Dec 07 '11 at 15:31
  • @DanielWagner: But you would still have to call it like `foo x (Proxy :: SomeType)`. Is that so much better than `foo x (undefined :: SomeType)`. I'd prefer undefined. – jmg Dec 07 '11 at 17:54
  • @jmg There's no accounting for taste, I guess. =) – Daniel Wagner Dec 07 '11 at 18:36
  • Never seen the `Proxy` take before. I like it more than `undefined` because I don't need to look at the API to see if the code will crash or not. – Thomas Eding Dec 07 '11 at 19:03