确实如此:
λ :i Applicative
class Functor f => Applicative (f :: * -> *) where
同时:
fmap f x = pure f <*> x
- 根据Applicative
的法律,我们可以从fmap
&amp;中定义pure
<*>
。
我不明白为什么每次我需要fmap
时都应该繁琐地定义Applicative
如果真的,fmap
可以根据pure
自动设置和<*>
。
如果pure
或<*>
以某种方式依赖于fmap
的定义,我认为有必要,但我不明白为什么必须这样做。
答案 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
。