这个问题可能看起来有些愚蠢,但我先问一下然后解释背后的理由。
让我们说我们通过以下方式重新定义Functor
:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
class Functor a where
generic_fmap :: a
instance Functor ((a -> b) -> Maybe a -> Maybe b) where
generic_fmap f (Just x) = Just (f x)
generic_fmap _ Nothing = Nothing
instance Functor ((a -> b) -> [a] -> [b]) where
generic_fmap = map
-- etc with more instances
然后,我已经考虑过两种方法来定义fmap
。
(1):
fmap :: (Functor ((a -> b) -> f a -> f b)) => (a -> b) -> f a -> f b
fmap = generic_fmap
或(2)(需要不可判定的实例)(已编辑):
class Functor f a b where
fmap :: (a -> b) -> f a -> f b
instance (GenericFunctor ((a -> b) -> f a -> f b)) => Functor f a b where
fmap = generic_fmap
除了增加的打字和丑陋之外,还有什么我们失去了以这两种方式定义fmap
?
我问的原因是我发现标准Functor
类比数学定义更具体,所以许多其他适合作为仿函数的东西不能成为仿函数哈斯克尔。我的想法是定义一个非常通用的Functor
类(尽管可能不像上面那样通用)。但是,通过使它过于通用,你会失去类型推断。因此,用户可以决定使用不同版本的fmap
,具体取决于它们是否需要通用性或类型推断。与用户如何在前奏中.
(仅适用于标准函数)或.
中的Control.Category
(适用于所有类别)之间进行选择非常相似。
我知道还有很多向后兼容性问题,但我的第一个问题是,我的定义是否与使用用户相同(不要担心那些想要定义实例的人)此时此刻)。
答案 0 :(得分:4)
据我们所知,即使使用UndecidableInstances
,您的定义也无法在当前的GHC中发挥作用。
问题是你需要一个假设的扩展" RankNConstraints
"这将允许您在上一个实例声明中提及a
和b
,尽管它们不在其中,例如如
instance (forall a b. GenericFunctor ((a -> b) -> f a -> f b)) => Functor f where
我已经想过,为了这个以及许多其他目的,这个扩展程序会很棒(例如Stackoverflow的答案会继续出现在看似相关的地方)。还有很多其他人,至少2000年SPJ和Ralph Hinze建议它in a paper。
不幸的是,我最近从old GHC ticket那里了解到,如何明智地实施它是因为它会对类型推断造成严重破坏。
答案 1 :(得分:2)
我认为这里更大的问题是Functor
不仅仅是map
,还有一些与之相关的法律。我不知道你如何能为你提出的GenericFunctor
表达法律。
例如,id
中的(.)
和Category
定义了身份和组合的含义 - 现在您可以为一对GenericFunctor
定义Categories
。