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 type ‘Float’ ‘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 (Float
s do that afaik)?
Constraints in Haskell mean one of the following things: Class constraints, e.g. Show a. Implicit parameter constraints, e.g. ? x::Int (with the -XImplicitParams flag) Equality constraints, e.g. a ~ Int (with the -XTypeFamilies or -XGADTs flag)
An instance of a class is an individual object which belongs to that class. In Haskell, the class system is (roughly speaking) a way to group similar types. (This is the reason we call them "type classes"). An instance of a class is an individual type which belongs to that class.
What's a typeclass in Haskell? A typeclass defines a set of methods that is shared across multiple types. For a type to belong to a typeclass, it needs to implement the methods of that typeclass. These implementations are ad-hoc: methods can have different implementations for different types.
The second line, deriving (Eq, Show) , is called the deriving clause; it specifies that we want the compiler to automatically generate instances of the Eq and Show classes for our Pair type. The Haskell Report defines a handful of classes for which instances can be automatically generated.
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 type ‘Float’
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)"
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...)
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