2017-04-27 62 views
2

的确如此:为什么Applicative应该继承Functor?

λ :i Applicative 
class Functor f => Applicative (f :: * -> *) where 

同时:

fmap f x = pure f <*> x 

- 由Applicative规律,我们可以从pure & <*>定义fmap

我不明白为什么我要我要一个Applicative如果,真的,fmap可以自动在pure<*>规定设立每次不厌其烦地定义fmap

我收集这将是必要的,如果pure<*>在某种程度上取决于fmap的定义,但我不明白他们为什么必须。

+3

你想让你的应用程序使用'fmap'吗?由于你描述的微不足道的实现,它没有理由继承'Functor'。 – 4castle

+0

是的,定义'fmap'是关于最简单的事情,你可以用任何可能的类型来完成。 – leftaroundabout

回答

1

虽然有建议,使其更容易https://ghc.haskell.org/trac/ghc/wiki/IntrinsicSuperclasses “默认实例”问题本身非常困难。

的一个挑战是如何应对常见的超:

fmap f x = pure f <*> x       -- using Applicative 
fmap f x = runIdentity (traverse (Identity . f) x) -- using Traversable 
fmap f x = x >>= (return . f)    -- using Monad 

哪一个来接?

所以我们现在能做的最好的就是提供fmapDefault(如Data.Traversable)那样;或使用pure f <*> x;或fmapRep,从Data.Functor.Rep适用。

+1

'fmap f x = x >> =(return。f)' – amalloy

7

虽然fmap可以从pure<*>得出,但它通常不是最有效的方法。比较:

fmap :: (a -> b) -> Maybe a -> Maybe b 
fmap f Nothing = Nothing 
fmap f (Just x) = Just (f x) 

使用应用型工具所做的工作:

fmap :: (a -> b) -> Maybe a -> Maybe b 
-- inlining pure and <*> in: fmap f x = pure f <*> x 
fmap f x = case (Just f) of 
    Nothing -> Nothing 
    Just f' -> case x of 
    Nothing -> Nothing 
    Just x' -> Just (f' x') 

在构造函数中无意义的东西包裹起来只是做一个模式匹配反对。

因此,显然能够独立于应用功能来定义fmap是有用的。那可能可以通过使用所有三个功能的单个类型类型来完成,使用可以覆盖的默认实现fmap。但是,有些类型可以制作出很好的Functor实例,但不是很好的Applicative实例,因此您可能只需要实现一个实例。因此,两个类型类。

而且由于没有适用实例但没有Functor实例的类型,您应该可以像处理Functor一样对待Applicative,如果您喜欢的话;因此两者之间的延伸关系。

但是,如果执行函子的轮胎,你可以(在大多数情况下)要求GHC导出唯一可能实现的函子的你,与

{-# LANGUAGE DeriveFunctor #-} 
data Boring a = Boring a deriving Functor 
+0

在大多数情况下,'数据Boring a =无聊派生Functor',一个独立的派生实例Functor Boring'仍然可以工作。 GHC不能以某种方式派生函子实例,即使在这种情况下,花生也会手工编写它,而与'Applicative'实例相比,真的很少有一个函数。 – leftaroundabout

+0

我希望GHC用'-O'来简化嵌套的情况,因为...''总是选择'Just'分支。事实上SPJ意味着它实际上在这个演讲中做了这个优化https://youtu.be/uR_VzYxvbxg?t=43m59s。当然,它不一定能够为所有应用程序进行重写。 –