2010-07-15 3 views
2

Я пытаюсь получить следующий код для компиляциивыведение типа многопараметрического типа класса

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) 

с ghc Main.hs -XMultiParamTypeClasses. Однако компилятор не может вывести тип переменной типа b (который в этом случае равен MyString). Как я могу прямо передать эту информацию компилятору?

ответ

5

Вы столкнулись с предположением о «открытом мире». В этом случае в области видимости имеется только один экземпляр, который может удовлетворять ограничениям типа; но это не очень декларативный способ указать значение myprint 3, не так ли? Учитывая, что экземпляры могут реально загружаться из любого модуля, мы можем видеть, как предположение открытого мира защищает вас от непредвиденных изменений типа/поведения, поскольку модули добавляются или обновляются.

В этом случае вы можете попробовать функциональные зависимости или тип семейств.

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

Чтобы расширить немного на ответ Джейсона:

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

означает, что b функционально зависит от a; то есть не может быть двух экземпляров этого класса с теми же a и разными b s.

3

Вместо функциональных зависимостей вы можете использовать семейства типов, которые эквивалентны прежним (по крайней мере теоретически), но, по-видимому, становятся более благоприятной формой определения отношения типа. В этом случае можно избежать использования классов с несколькими параметрами:

{-# 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)