deducing type of multi-parameter type class
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 cann开发者_开发问答ot deduce the type of the b
type variable (which in this case is MyString
). How can I explicitly tell this information to the compiler?
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
...
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 b
s.
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)
精彩评论