tags:

views:

60

answers:

3

I'm trying to get the following code to compile

import IO

data MyInt = MyInt Int
data MyString = MyString String deriving Show

class Show b => MyClass a b where
    fn :: a -> b

instance MyClass MyInt MyString where
    fn (MyInt i) = MyString (show i)

myprint :: (MyClass a b) => a -> IO () 
myprint a = putStrLn $ show (fn a)

main = myprint (MyInt 3)

with ghc Main.hs -XMultiParamTypeClasses. However, the compiler cannot deduce the type of the b type variable (which in this case is MyString). How can I explicitly tell this information to the compiler?

+4  A: 

You've run afoul of the "open world" assumption. In this case, there's only one instance in scope that can satisfy the type constraints; but that's not a very declarative way to specify the meaning of myprint 3, is it? Given that instances can float in really from any module whatsoever, we can see how the open world assumption protects you from unanticipated changes of type/behaviour as modules are added or updated.

You could try functional dependencies or type families in this case.

class Show b => MyClass a b | a -> b where
  ...
Jason Dusek
+1  A: 

To expand a bit on Jason's answer:

class Show b => MyClass a b | a -> b where
  ...

means that b functionally depends on a; that is, there can be no two instances of this class with the same a and different bs.

Alexey Romanov
+1  A: 

Instead of functional dependencies you can use type families which are equivalent to the former (at least theoretically) but seem to becoming more favorable form of type relationship definition. You can even avoid multi-parameter type classes in this case:

{-# LANGUAGE TypeFamilies #-}

data MyInt = MyInt Int
data MyString = MyString String deriving Show

class MyClass a where
    type Showable a :: *
    fn :: a -> Showable a

instance MyClass MyInt where
    type Showable MyInt = MyString
    fn (MyInt i) = MyString (show i)

myprint a = putStrLn $ show (fn a)

main = myprint (MyInt 3) 
Ed'ka