Haskell 如何在需要类型构造函数而不是具体类型的类实例中统计类约束?
我目前在of中,我已经到达了Haskell 如何在需要类型构造函数而不是具体类型的类实例中统计类约束?,haskell,type-constructor,Haskell,Type Constructor,我目前在of中,我已经到达了Functor类型类的部分。在上述章节中,作者举例说明了如何将不同类型作为类的实例(例如Maybe、自定义树类型等)。看到这一点,我决定(为了好玩和练习)尝试为该类型实现一个实例;当然,在所有这一切中,我们都忽略了 实际的实例本身非常简单,我这样写: instance Functor Set.Set where fmap f empty = Set.empty fmap f s = Set.fromList $ map f (Set.elems s) 但
Functor
类型类的部分。在上述章节中,作者举例说明了如何将不同类型作为类的实例(例如Maybe
、自定义树
类型等)。看到这一点,我决定(为了好玩和练习)尝试为该类型实现一个实例;当然,在所有这一切中,我们都忽略了
实际的实例本身非常简单,我这样写:
instance Functor Set.Set where
fmap f empty = Set.empty
fmap f s = Set.fromList $ map f (Set.elems s)
但是,由于我碰巧使用了该函数,因此引入了一个类约束,要求集合
中使用的类型为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
见:
我尝试对实例施加约束,或向fmap
添加类型签名,但都没有成功(都是编译器错误)
在这种情况下,如何满足约束条件?有什么可能的办法吗
提前感谢!:) 不幸的是,对于标准的Functor
类,没有简单的方法可以做到这一点。这就是为什么默认情况下,Set
不附带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
基本上,您在这里所做的就是使集中的类型也成为类的一部分。这样,您就可以在编写该类的实例时约束这些类型
此版本的Functor
需要两个扩展:MultiParamTypeClasses
和FlexibleInstances
。(您需要第一个扩展来定义类,第二个扩展来定义Set
的实例)
对此进行了很好的讨论。这是不可能的。Functor
类的用途是,如果您有Functor f=>fa
,则可以用任何您喜欢的内容替换a
。不允许该类约束您只返回这个或那个。由于Set
要求其元素满足某些约束(实际上,这不是实现细节,而是集合的基本属性),因此它不满足Functor
的要求
正如在另一个答案中提到的,有一些方法可以开发一个类,比如Functor
,它确实以这种方式约束您,但它实际上是一个不同的类,因为它为类的用户提供了更少的保证(您不能将它与您想要的任何类型参数一起使用),作为交换,它将适用于更广泛的类型。毕竟,这是定义类型属性的经典权衡:您想要满足的类型越多,它们必须满足的越少
(另一个有趣的例子是MonadPlus
类。特别是,对于每个实例MonadPlus TC
,您可以创建一个实例Monoid(tca)
,但您不能总是反过来。因此,Monoid(可能是a)
实例可能不同于MonadPlus
实例,因为前者可以限制a
,但后者不能。)
约束函子类的一个问题是,我们失去了很多能力。特别是对于应用函子,我们通常希望将函数放入其中。然而,在许多情况下,不可能为函数提供我们感兴趣的类的一般实例。
{-# LANGUAGE GADTs #-}
data CYSet a where
CYSet :: (Ord a) => Set.Set a -> (a -> b) -> CYSet b
liftCYSet :: (Ord a) => Set.Set a -> CYSet a
liftCYSet s = CYSet s id
lowerCYSet :: (Ord a) => CYSet a -> Set.Set a
lowerCYSet (CYSet s f) = Set.fromList $ map f $ Set.elems s
instance Functor CYSet where
fmap f (CYSet s g) = CYSet s (f . g)
main = putStrLn . show
$ lowerCYSet
$ fmap (\x -> x `mod` 3)
$ fmap abs
$ fmap (\x -> x - 5)
$ liftCYSet $ Set.fromList [1..10]
-- prints "fromList [0,1,2]"