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")
。有没有诀窍可以避免需要类型签名?
答案 0 :(得分:11)
基本上问题是设置a
的类型并不能告诉编译器b
的类型是什么。您可能想,因为该类只有一个实例(其中a
为MyString
且b
为Char
),但任何人都可以随时添加新实例。因此,只有一个实例现在的事实无助于编译器决定您想要的类型。
解决方法是使用功能依赖关系或类型系列。后者是较新的解决方案,旨在最终“替换”前者,但现在两者仍然得到完全支持。 FD是否会消失还有待观察。无论如何,FDs:
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
就像一个类方法,它采用listy类型并返回相应的元素类型。然后你可以做
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
对于上述所有类型都有意义;这些只是如何定义类的示例。)
答案 1 :(得分: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