Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: class instance with class type parameters

Tags:

haskell

Good day. I'm new to Haskell. One thing is not clear for me concerning declaring and instantiating some custom classes.

  1. There is a standard class Integral in haskell. According to the hackage, Integral declares the mandatory method quot :: a -> a -> a. So it means that every instance of that class should have this method implementation, right?

  2. We can declare some function, using Integral as an argument, like:

proba :: (Integral a) => a -> a -> a
proba x y = x `quot` y

So far so good

  1. Now lets declare our own class Proba:
class Proba a where
    proba :: a -> a -> a

I can implement an Int or Integer (or other data type) instance like this:

instance Proba Integer where
    proba x y = x `quot` y

instance Proba Int where
    proba x y = x `quot` y

But I don't want to. I want one instance for every Integral. But when I try to do it, I get an error:

instance (Integral a) => Proba a where
    proba x y = x `quot` y

 Illegal instance declaration for `Proba a'
   (All instance types must be of the form (T a1 ... an)
    where a1 ... an are *distinct type variables*,
    and each type variable appears at most once in the instance head.
    Use FlexibleInstances if you want to disable this.)
 In the instance declaration for `Proba a'

Ok, it seems that it asks me for distinct type variables instead of classes. But why?! Why isn't it enough for it just to have an Integral here? Since quot is declared for every Integral, this instance should be valid for every Integral, shoudn't it?

Maybe there is a way to achieve the same effect?

like image 478
skapral Avatar asked Sep 10 '14 15:09

skapral


1 Answers

As the error message indicates, you can use FlexibleInstances (a fairly common and safe extension) to allow this behavior, but you'll also need UndecidableInstances:

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}

class Proba a where
    proba :: a -> a -> a

instance Integral a => Proba a where
    proba = quot

The reason why this isn't enabled by default is because it's specifically a GHC extension, and it is not part of the Haskell98 specification. You'll find that there are a lot of language extensions that are very useful and safe to use, and often times you only want them enabled in particular modules. Instead of just asking "why isn't this default", also ask "when would I not want this to be default?".


Another way to implement this without extensions is to encode the type class directly as a data type:

data Proba a = Proba
    { proba :: a -> a -> a
    }

integralProba :: Integral a => Proba a
integralProba = Proba quot

Then you can pass it around as

foldProba :: Proba a -> a -> [a] -> a
foldProba p = foldr (proba p)

Then if you have foldProba integralProba, then it automatically constrains the type to Integral a => a -> [a] -> a.

like image 149
bheklilr Avatar answered Nov 07 '22 10:11

bheklilr