Applicative为什么要从Functor继承?

时间:2017-04-27 07:23:18

标签: haskell functor applicative

确实如此:

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

同时:

fmap f x = pure f <*> x

- 根据Applicative的法律,我们可以从fmap&amp;中定义pure <*>

我不明白为什么每次我需要fmap时都应该繁琐地定义Applicative如果真的,fmap可以根据pure自动设置和<*>

如果pure<*>以某种方式依赖于fmap的定义,我认为有必要,但我不明白为什么必须这样做。

2 个答案:

答案 0 :(得分:7)

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

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

使用Applicative工具完成工作:

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')

在构造函数中无意义地包装某些内容,只是为了对它进行模式匹配。

因此,显然能够独立于Applicative函数定义fmap是有用的。 可以通过使用所有三个函数创建一个类型类来完成,使用您可以覆盖的fmap的默认实现。但是,有些类型可以构建良好的Functor实例但不是良好的Applicative实例,因此您可能只需要实现一个。因此,两个类型。

由于没有带有Applicative实例但没有Functor实例的类型,如果你愿意,你应该可以将Applicative视为Functor;因此两者之间的延伸关系。

但是,如果您厌倦了实施Functor,您可以(在大多数情况下)要求GHC为您推导出唯一可能的Functor实现,

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

答案 1 :(得分: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