Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you apply function constraints in instance methods in Haskell?

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 (Floats do that afaik)?

like image 739
FK82 Avatar asked Jun 02 '16 10:06

FK82


People also ask

What is a constraint in Haskell?

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)

What are instances in Haskell?

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 is a Typeclass in Haskell?

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.

How does deriving work in Haskell?

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.


2 Answers

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)"

like image 50
chi Avatar answered Oct 02 '22 07:10

chi


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

like image 42
MathematicalOrchid Avatar answered Oct 02 '22 05:10

MathematicalOrchid