man*_*old 7 haskell functional-programming
我在 Haskell 邮件列表上遇到了这个讨论。从讨论来看,添加liftA2 作为Applicative 的方法似乎对性能有影响。你能提供具体的例子,为什么需要在 Applicative 方法中添加 liftA2 吗?
Wil*_*sem 10
邮件是 2017 年写的。 当时的Applicativetypeclass是这样的:
Run Code Online (Sandbox Code Playgroud)class Functor f => Applicative f where -- | Lift a value. pure :: a -> f a -- | Sequential application. (<*>) :: f (a -> b) -> f a -> f b -- | Sequence actions, discarding the value of the first argument. (*>) :: f a -> f b -> f b a1 *> a2 = (id <$ a1) <*> a2 -- This is essentially the same as liftA2 (const id), but if the -- Functor instance has an optimized (<$), we want to use that instead. -- | Sequence actions, discarding the value of the second argument. (<*) :: f a -> f b -> f a (<*) = liftA2 const
所以没有liftA2作为Applicative类型类的一部分。它被定义为 [src]:
Run Code Online (Sandbox Code Playgroud)liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c liftA2 f a b = fmap f a <*> b
因此无法在类型类中进行特殊实现。这意味着有时liftA2可以更有效地实施,但无法定义。
例如Maybe函子和Applicative被实现为:
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap _ Nothing = Nothing
instance Applicative Maybe where
pure = Just
Just f <*> Just x = Just (f x)
_ <*> _ = Nothing
Run Code Online (Sandbox Code Playgroud)
因此,这意味着liftA2for aMaybe的实现类似于:
liftA2Maybe :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
liftA2Maybe f x y = apMaybe (fmapMaybe f x) y
where fmapMaybe f (Just x) = Just (f x)
fmapMaybe _ Nothing = Nothing
apMaybe (Just f) (Just x) = Just (f x)
apMaybe _ _ = Nothing
Run Code Online (Sandbox Code Playgroud)
但这不是最优的。这意味着fmapMaybe将检查参数是否为 aJust x或Nothing,然后返回 aJust (f x)或 a Nothing。但无论如何,apMaybe将再次检查,而我们已经可以提前知道了。我们可以通过以下方式进行更有效的实施:
liftA2Maybe :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
liftA2Maybe f (Just x) (Just y) = Just (f x y)
liftA2Maybe _ _ _ = Nothing
Run Code Online (Sandbox Code Playgroud)
在这里,我们避免了对数据构造函数的额外解包。然而,这并不是那么有问题。对于像 a 这样的某些数据结构ZipList,开销会更严重,因为对象的数量更大。
2017 年 6 月 23 日,发布了一个新base库,其中将liftA2函数作为方法添加到Applicative类型 class 中。