8
votes

I'm learning how to use typeclasses in Haskell.

Consider the following implementation of a typeclass T with a type constrained class function f.

class T t where
    f :: (Eq u) => t -> u 

data T_Impl = T_Impl_Bool Bool | T_Impl_Int Int | T_Impl_Float Float 
instance T T_Impl where
    f (T_Impl_Bool x) = x
    f (T_Impl_Int x) = x
    f (T_Impl_Float x) = x

When I load this into GHCI 7.10.2, I get the following error:

Couldn't match expected type ‘u’ with actual typeFloat’
      ‘u’ is a rigid type variable bound by
          the type signature for f :: Eq u => T_Impl -> u 
          at generics.hs:6:5

Relevant bindings include
  f :: T_Impl -> u (bound at generics.hs:6:5)
In the expression: x
In an equation for ‘f’: f (T_Impl_Float x) = x

What am I doing/understanding wrong? It seems reasonable to me that one would want to specialize a typeclass in an instance by providing an accompaning data constructor and function implementation. The part

Couldn't match expected type 'u' with actual type 'Float'

is especially confusing. Why does u not match Float if u only has the constraint that it must qualify as an Eq type (Floats do that afaik)?

3
the constraint is not your problem - the problem is that you are saying: no matter what Eq type you want from me as a result - I can provide itRandom Dev
Where do I say that?FK82
in f :: Eq u => t -> u - t is know from the context - the u is there with an implicit forallRandom Dev
@Carsten Can you link me to a reference for your claim. From what I understand a type constraint only limits potential definitions.FK82
it's not about the constraint - it's about the u - it's really just what Chi pointed out in his answer and commentsRandom Dev

3 Answers

18
votes

The signature

f :: (Eq u) => t -> u 

means that the caller can pick t and u as wanted, with the only burden of ensuring that u is of class Eq (and t of class T -- in class methods there's an implicit T t constraint).

It does not mean that the implementation can choose any u.

So, the caller can use f in any of these ways: (with t in class T)

f :: t -> Bool 
f :: t -> Char
f :: t -> Int
... 

The compiler is complaining that your implementation is not general enough to cover all these cases.

Couldn't match expected type ‘u’ with actual typeFloat

means "You gave me a Float, but you must provide a value of the general type u (where u will be chosen by the caller)"

9
votes

Chi has already pointed out why your code doesn't compile. But it's not even that typeclasses are the problem; indeed, your example has only one instance, so it might just as well be a normal function rather than a class.

Fundamentally, the problem is that you're trying to do something like

foobar :: Show x => Either Int Bool -> x
foobar (Left  x) = x
foobar (Right x) = x

This won't work. It tries to make foobar return a different type depending on the value you feed it at run-time. But in Haskell, all types must be 100% determined at compile-time. So this cannot work.

There are several things you can do, however.

First of all, you can do this:

foo :: Either Int Bool -> String
foo (Left  x) = show x
foo (Right x) = show x

In other words, rather than return something showable, actually show it. That means the result type is always String. It means that which version of show gets called will vary at run-time, but that's fine. Code paths can vary at run-time, it's types which cannot.

Another thing you can do is this:

toInt :: Either Int Bool -> Maybe Int
toInt (Left  x) = Just x
toInt (Right x) = Nothing

toBool :: Either Int Bool -> Maybe Bool
toBool (Left  x) = Nothing
toBool (Right x) = Just x

Again, that works perfectly fine.

There are other things you can do; without knowing why you want this, it's difficult to suggest others.

As a side note, you want to stop thinking about this like it's object oriented programming. It isn't. It requires a new way of thinking. In particular, don't reach for a typeclass unless you really need one. (I realise this particular example may just be a learning exercise to learn about typeclasses of course...)

0
votes

It's possible to do this:

class Eq u => T t u | t -> u where
    f :: t -> u

You need FlexibleContextx+FunctionalDepencencies and MultiParamTypeClasses+FlexibleInstances on call-site. Or to eliminate class and to use data types instead like Gabriel shows here