Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle functions of a multi-parameter typeclass, who not need every type of the typeclass?

Tags:

haskell

I've defined a typeclass similar to an interface with a bunch of functions required for my program. Sadly, it needs multiple polymorphic types, but not every function of this multi-parameter typeclass needs every type. GHC haunts me with undeduceable types and i can't get the code running.

A reduced example:

{-# LANGUAGE MultiParamTypeClasses #-}

class Foo a b where
    -- ...
    bar :: a -> ()

baz :: Foo a b => a -> ()
baz = bar

GHC says

Possible fix: add a type signature that fixes these type variable(s)

How can I do this for b? Especially when I want to keep b polymorphic. Only an instance of Foo should define what this type is.

like image 696
Vektorweg Avatar asked Jun 01 '14 19:06

Vektorweg


2 Answers

This is impossible.

The underlying problem is that a multiparameter type class depends on every type parameter. If a particular definition in the class doesn't use every type parameter, the compiler will never be able to know what instance you mean, and you'll never even be able to specify it. Consider the following example:

class Foo a b where
    bar :: String -> IO a

instance Foo Int Char where
    bar x = return $ read x

instance Foo Int () where
    bar x = read <$> readFile x

Those two instances do entirely different things with their parameter. The only way the compiler has to select one of those instances is matching both type parameters. But there's no way to specify what the type parameter is. The class is just plain broken. There's no way to ever call the bar function, because you can never provide enough information for the compiler to resolve the class instance to use.

So why is the class definition not rejected by the compiler? Because you can sometimes make it work, with the FunctionalDependencies extension.

If a class has multiple parameters, but they're related, that information can sometimes be added to the definition of the class in a way that allows a class member to not use every type variable in the class's definition.

class Foo a b | a -> b where
    bar :: String -> IO a

With that definition (which requires the FunctionalDependencies extension), you are telling the compiler that for any particular choice of a, there is only one valid choice of b. Attempting to even define both of the above instances would be a compile error.

Given that, the compiler knows that it can select the instance of Foo to use based only on the type a. In that case, bar can be called.

like image 116
Carl Avatar answered Sep 24 '22 02:09

Carl


Splitting it in smaller typeclasses might be sufficient.

{-# LANGUAGE MultiParamTypeClasses #-}

class Fo a => Foo a b where
    -- ...
    foo :: a -> b -> ()


class Fo a where
    bar :: a -> ()


baz :: Foo a b => a -> ()
baz = bar
like image 38
Vektorweg Avatar answered Sep 24 '22 02:09

Vektorweg