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.
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.
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
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