Applicative为什么要继承Functor?
Why should Applicative inherit from Functor?
确实如此:
λ :i Applicative
class Functor f => Applicative (f :: * -> *) where
同时:
fmap f x = pure f <*> x
— 根据 Applicative
的定律,我们可以从 pure
& <*>
.
定义 fmap
我不明白为什么每次我想要 Applicative
时我都要繁琐地定义 fmap
如果真的 fmap
可以根据 [=14] 自动设置=] 和 <*>
.
我猜想如果 pure
或 <*>
以某种方式依赖于 fmap
的定义,但我不明白为什么他们必须这样做。
虽然 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 实例,因此您可能只需要实现一个。因此,两个类型类。
并且由于没有类型具有 Applicative 实例但没有 Functor 实例,如果您愿意,您应该能够将 Applicative 视为 Functor;因此两者之间的扩展关系。
但是,如果您厌倦了实现 Functor,您可以(在大多数情况下)要求 GHC 为您派生 Functor 的唯一可能实现,
{-# LANGUAGE DeriveFunctor #-}
data Boring a = Boring a deriving Functor
虽然有一些建议可以使它变得更容易 https://ghc.haskell.org/trac/ghc/wiki/IntrinsicSuperclasses,但 "default instances" 问题本身非常困难。
一个挑战是如何处理公共超类:
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
(如果适用)。
确实如此:
λ :i Applicative
class Functor f => Applicative (f :: * -> *) where
同时:
fmap f x = pure f <*> x
— 根据 Applicative
的定律,我们可以从 pure
& <*>
.
fmap
我不明白为什么每次我想要 Applicative
时我都要繁琐地定义 fmap
如果真的 fmap
可以根据 [=14] 自动设置=] 和 <*>
.
我猜想如果 pure
或 <*>
以某种方式依赖于 fmap
的定义,但我不明白为什么他们必须这样做。
虽然 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 实例,因此您可能只需要实现一个。因此,两个类型类。
并且由于没有类型具有 Applicative 实例但没有 Functor 实例,如果您愿意,您应该能够将 Applicative 视为 Functor;因此两者之间的扩展关系。
但是,如果您厌倦了实现 Functor,您可以(在大多数情况下)要求 GHC 为您派生 Functor 的唯一可能实现,
{-# LANGUAGE DeriveFunctor #-}
data Boring a = Boring a deriving Functor
虽然有一些建议可以使它变得更容易 https://ghc.haskell.org/trac/ghc/wiki/IntrinsicSuperclasses,但 "default instances" 问题本身非常困难。
一个挑战是如何处理公共超类:
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
(如果适用)。