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

现在我需要写 eg lifted (reverse::(String -> String)) (MyString "Foobar")。是否有避免需要类型签名的技巧?

4

2 回答 2

11

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

对此的解决方案是使用功能依赖项或类型族。后者是较新的解决方案,旨在最终“取代”前者,但目前两者仍然得到完全支持。FD是否会消失还有待观察。无论如何,对于 FD:

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

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

替代方案是 TF:

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不一定对上述所有类型都有意义;这些只是如何定义类的示例。)

于 2012-10-17T08:09:15.553 回答
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
于 2012-10-17T07:32:55.997 回答