如何避免在这里使用显式类型签名?

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") 的代码。有什么技巧可以避免需要类型签名吗?

2个回答

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

解决此问题的方法是使用函数依赖或类型族。后者是较新的解决方案,并旨在最终“取代”前者,但目前两者仍得到完全支持。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,而是称为元素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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接