2012-10-17 55 views
6
class Listy a b where 
    fromList :: [b] -> a 
    toList :: a -> [b] 
    lifted :: ([b] -> [b]) -> (a -> a) 
    lifted f = fromList . f . toList 

data MyString = MyString { getString :: String } deriving Show 

instance Listy MyString Char where 
    toList = getString 
    fromList = MyString 

现在我需要编写例如lifted (reverse::(String -> String)) (MyString "Foobar")。有没有一种方法可以避免使用类型签名?如何避免显式类型签名?

回答

11

本质的问题是,设置类型的a不告诉编译器什么的b的类型。您可能认为,因为只有一个类的实例(其中aMyStringbChar),但任何人都可以随时添加新实例。所以只有一个实例现在并不能帮助编译器决定你想要的类型。

解决方法是使用功能依赖项或类型族。后者是较新的解决方案,旨在最终“取代”前者,但现在两者仍得到完全支持。 FD是否消失仍有待观察。无论如何,与文件描述符:

class Listy a b | a -> b where ... 

本质上讲,这表示“没有永远只能是每个a一个类的实例”。换句话说,一旦你知道a,你总是可以确定b。 (但不是相反。)其他人看起来像以前一样。

另一种方法是转录因子:

class Listy a where 
    type Element a :: * 
    ... 

instance Listy MyString where 
    type Element MyString = Char 
    ... 

现在不是第二类被称为b,这就是所谓的Element a。单词Element就像一个类方法,它接受一个列表类型并返回相应的元素类型。你可以再做

instance Listy ByteString where 
    type Element ByteString = Word8 
    ... 

instance Listy [x] where 
    type Element [x] = x 
    ... 

instance Ord x => Listy (Set x) where 
    type Element (Set x) = x 
    ... 

等等。 (并非Listy对上述所有类型都是必须的;这些只是如何定义类的例子。)

5

您可以尝试-XFunctionalDependencies

class Listy a b | a -> b where 
    fromList :: [b] -> a 
    toList :: a -> [b] 
    lifted :: ([b] -> [b]) -> (a -> a) 
    lifted f = fromList . f . toList