为了练习,我一直在尝试仅使用功能ap和liftM来实现liftM2。这些功能定义为:
<div class="swiper-container">
<div class="swiper-slides"></div>
</div>
<div class="swiper-button-prev-unique"></div>
<div class="swiper-button-next-unique"></div>
let carousel = new Swiper('.swiper-container', {
navigation: {
nextEl: '.swiper-button-next-unique',
prevEl: '.swiper-button-prev-unique'
}
});
我可以轻松地使用do表示法来完成liftM2,但是不确定如何仅使用ap和liftM来完成。我当时想让结果看起来像这样:
ap :: IO (a -> b) -> IO a -> IO b
liftM :: (a -> b) -> IO a -> IO b
liftM2 :: (a -> b -> c) -> IO a -> IO b -> IO c
我对如何弄乱f感到困惑,f是(a-> b-> c),这样我就可以将a转到b并将b转到c。谢谢。
答案 0 :(得分:6)
一般模式正在转变
liftMn f a1 ... an
进入
f <$> a1 <*> ... <*> an
-- i.e., more precisely
(... ((f <$> a1) <*> a2) ... <*> an)
其中<$>
是liftM
(又名fmap
),而<*>
是ap
。
因此,对于n=2
,我们得到
(f `liftM` a1) `ap` a2
-- i.e.
ap (liftM f a1) a2
检查类型:
f :: t1 -> t2 -> r
liftM f :: IO t1 -> IO (t2 -> r)
a1 :: IO t1
liftM f a1 :: IO (t2 -> r)
ap (liftM f a1) :: IO t2 -> IO r
a2 :: IO t2
ap (liftM f a1) a2 :: IO r
此处的关键思想是将f :: t1 -> t2 -> r
读为f :: t1 -> (t2 -> r)
,以便跟随liftM f :: IO t1 -> IO (t2 -> r)
。注意IO
内的函数类型。然后,我们可以使用IO
在->
上“分发” ap
,以便我们可以应用a2 :: IO t2
。
答案 1 :(得分:0)
我认为值得注意的是您的最初猜测,
liftM2 f a b = liftM (_) (ap _ a)
并不是真的那么远。但是ap
并不是开始使用该形状的正确位置。而是考虑
pairIO :: IO a -> IO b -> IO (a, b)
pairIO m n = do
a <- m
b <- n
return (a, b)
现在,您可以写
liftM2 :: (a -> b -> c) -> IO a -> IO b -> IO c
liftM2 f m n = liftM _ (pairIO m n)
GHC会告诉您它的需要
_ :: (a, b) -> c
您应该能够非常轻松地完成此操作。
这实际上反映了“应用函子”概念的常见替代表达:
class Functor f => Monoidal f where
pur :: a -> f a
pair :: f a -> f b -> f (a, b)
该类的能力与标准Applicative
类相同。
事实证明,由于Monoidal
关联律,您实际上可以以各种方式组合动作。看起来像
xs `pair` (ys `pair` zs) = jigger <$> ((xs `pair` ys) `pair` z's)
where
jigger ((x, y), z) = (x, (y, z))
答案 2 :(得分:0)
有了ap :: IO (a -> b) -> IO a -> IO b
,我们都拥有
IO (a -> b) {- and -} IO (a -> b -> c)
IO a IO a
----------- ----------------
IO b IO (b -> c)
因此我们可以通过IO二进制函数将两个IO值组合在一起
ap2 :: IO (a -> b -> c) -> IO a -> IO b -> IO c
ap2 mf mx my = ap mf mx `ap` my
IO (a -> b -> c)
IO a
----------------
IO (b -> c)
IO b
----------------
IO c
或具有纯二进制函数,
liftM2 :: (a -> b -> c) -> IO a -> IO b -> IO c
liftM2 f mx my = ap2 (return f) mx my
= ap (return f) mx `ap` my
= (ap . return) f mx `ap` my
ap . return
是什么类型?
> :t ap . return
ap . return :: Monad m => (a -> b) -> m a -> m b
为什么是liftM
的类型! (此处为更具体的(a -> b -> c) -> IO a -> IO (b -> c)
)
= liftM f mx `ap` my