在 Haskell 中,(liftM .liftM)、(liftM .liftM .liftM) 等有别名吗?
这样我就不必那么冗长了,例如:
(liftM . liftM) (+ 1) [Just 1, Just 2] = [Just 2, Just 3]
(liftM2 . liftM2) (+) [Just 1] [Just 2] = [Just 3]
base 中没有这样的东西,但在 Stack Overflow 上问了我一段时间以来最有趣的问题做得很好。
Functors 和 Applicative functors 在组合下是封闭的(对于 monad,当然不是一般情况,因此需要 monad 转换器),这就是为什么liftA2.liftA2
在这里工作,liftM2
通常只是liftA2
,尤其是现在 Applicative 正在成为 Monad 的超类。
您可以使用Data.Functor.Compose 包中的组合 newtype来组合 Applicative,但是您也可以通过其他方式从旧的 applicative 中创建新的 applicative - 我强烈推荐Gershom Bazerman 在 Comonad Reader forfolk中的文章“Abstracting with Applicatives”想要了解组合的 Applicative 结构与 monad 转换器堆栈相比有多漂亮 - 我现在一直在寻找让事情变得只是 Applicative 而不是 monadic 的东西,在那里我可以获得我需要的功能。通常我可以使用 Applicative 将所有输入内容组合成一个我想要输出的值,然后将它直接传递到我要使用的地方>>=
。
当然,没有什么能阻止你定义自己的函数:
liftliftA2 :: (Applicative f, Applicative g) =>
(a -> b -> c) -> f (g a) -> f (g b) -> f (g c)
liftliftA2 = liftA2.liftA2
但它并不比liftA2.liftA2
.
不过,我喜欢你制作嵌套 Applicative 运算符的想法,但会切换到增加尖括号而不是重复内部运算符,因为与Control.Applicative<**>
冲突,而且它更合乎逻辑。(<**>) = flip (<*>)
import Control.Applicative
(<<$>>) :: (Functor f, Functor g) =>
(a -> b) -> f (g a) -> f (g b)
(<<$>>) = fmap.fmap
(<<*>>) :: (Functor m, Applicative m, Applicative n) =>
m (n (a -> b)) -> m (n a) -> m (n b)
mnf <<*>> mna = (<*>) <$> mnf <*> mna
给予
ghci> (+) <<$>> [Just 5] <<*>> [Just 7,Just 10]
[Just 12,Just 15]
当然你可以继续:
(<<<$>>>) :: (Functor f, Functor g, Functor h) =>
(a -> b) -> f (g (h a)) -> f (g (h b))
(<<<$>>>) = fmap.fmap.fmap
(<<<*>>>) :: (Functor l,Functor m, Applicative l, Applicative m, Applicative n) =>
l (m (n (a -> b))) -> l (m (n a)) -> l (m (n b))
lmnf <<<*>>> lmna = (<*>) <<$>> lmnf <<*>> lmna
这可以让你做明显不可能的事情
ghci> subtract <<<$>>> Right [Just 5,Nothing,Just 10] <<<*>>> Right [Just 100,Just 20]
Right [Just 95,Just 15,Nothing,Nothing,Just 90,Just 10]
但是话又说回来,正如 Gershom Bazerman 的文章所展示的,您可能想要嵌套 Applicative,就像您想要嵌套 Monad 一样深。