我目前位于Chapter 8的Learn you a Haskell,我已经到达了Functor
类型类的部分。在上述部分中,作者举例说明了如何使不同的类型成为类的实例(例如Maybe
,自定义Tree
类型等等。)看到这一点,我决定(为了好玩和练习)尝试实现Data.Set
类型的实例;当然,在所有这些中忽略Data.Set.map
。
实际的实例本身很简单,我把它写成:
instance Functor Set.Set where
fmap f empty = Set.empty
fmap f s = Set.fromList $ map f (Set.elems s)
但是,因为我碰巧使用函数fromList
,这会引入一个类约束,调用Set
中使用的类型为Ord
,如编译器错误所解释的那样:
Error occurred
ERROR line 4 - Cannot justify constraints in instance member binding
*** Expression : fmap
*** Type : Functor Set => (a -> b) -> Set a -> Set b
*** Given context : Functor Set
*** Constraints : Ord b
请参阅:Live Example
我尝试在实例上添加约束,或者向fmap
添加类型签名,但都没有成功(两者都是编译器错误。)
鉴于这种情况,如何实现约束并满足约束?有什么办法吗?
提前致谢! :)
答案 0 :(得分:15)
不幸的是,没有使用标准Functor
类轻松实现此目的。这就是Set
默认情况下没有Functor
实例的原因:你不能写一个。
这是一个问题,并且有一些建议的解决方案(例如以不同的方式定义Functor
类),但我不知道是否就如何最好地处理这个问题达成共识。
我相信一种方法是使用constraint kinds重写Functor
类来重新定义新Functor
类可能具有的其他约束实例。这样您就可以指定Set
必须包含Ord
类中的类型。
另一种方法仅使用多参数类。我只能找到关于为Monad
课程执行此操作的文章,但使Set
Monad
部分Functor
面临与class Functor' f a b where
fmap' :: (a -> b) -> f a -> f b
instance (Ord a, Ord b) => Functor' Data.Set.Set a b where
fmap' = Data.Set.map
的一部分相同的问题。它被称为Restricted Monads。
在这里使用多参数类的基本要点似乎是这样的:
Set
基本上,你在这里所做的只是将中的类型作为类Functor
的一部分。然后,这可以让您在编写该类的实例时约束这些类型的内容。
此版本的MultiParamTypeClasses
需要两个扩展程序:FlexibleInstances
和Set
。 (您需要第一个扩展才能定义类,第二个扩展需要能够为{{1}}定义实例。)
Haskell : An example of a Foldable which is not a Functor (or not Traversable)?对此进行了很好的讨论。
答案 1 :(得分:2)
这是不可能的。 Functor
课程的目的是,如果您有Functor f => f a
,则可以将a
替换为您喜欢的任何内容。不允许该类限制您仅返回此或那个。由于Set
要求其元素满足某些约束(实际上这不是实现细节,但实际上是集合的基本属性),因此它不满足Functor
的要求。
正如另一个答案所提到的那样,开发像Functor
这样的类的方法会以这种方式限制你,但它实际上是一个不同的类,因为它给了类的用户更少的保证(你不要将它用于你想要的任何类型参数,以换取适用于更广泛的类型。也就是说,毕竟定义类型属性的经典权衡:你想要满足它的类型越多,它们必须被迫满足的越少。
(另一个有趣的示例显示的是MonadPlus
类。特别是,对于每个实例MonadPlus TC
,您可以创建一个实例Monoid (TC a)
,但您不能总是去因此,Monoid (Maybe a)
实例与MonadPlus Maybe
实例不同,因为前者可以限制a
,但后者不能。{/ p>
答案 2 :(得分:0)
You can do this using a CoYoneda Functor.
public string GetDiscount(int CustomerId)
{
//return something
}