最佳答案绝对是正确的,并且仅从类型就可以快速有效地工作。一旦你擅长 Haskell(免责声明:我不是),那么这是一种比通过函数定义更有效的方式来理解这一点。
ap
但是由于我最近在处理The Monad Challenges时不得不努力解决这个问题,所以我决定分享我的经验,因为它可能会提供一些额外的直觉。
首先,正如 The Monad Challenges 所要求的,我将使用这个名称bind
来指代主要的 Monad 运算符>>=
。我认为这很有帮助。
如果我们定义自己的版本,liftM2
我们可以这样做:
liftM2 :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c
liftM2 f ma mb =
ma `bind` \a ->
mb `bind` \b ->
return $ f a b
我们想ap
用它来帮助我们创建。让我们f
暂时搁置这个函数,想想它是如何工作的,ap
假设我们选择了正确的函数f
。
假设我们要传递一个函数值 Monad 作为上面的第一部分,即ma
部分。它可能是某种东西,Just (+3)
或者[(+3), (*2)]
——一些生活在 Monad 上下文中的“功能”。
我们提供了一个参数值 Monad 作为第二部分mb
,例如Just 5
or [6,7,8]
-- 一些生活在 Monad 上下文中的“值”,它可以作为生活在里面的函数的参数ma
。
那我们就有
liftM2 f (m someFunction) (m someArgument) =
(m someFunction) `bind` \a ->
(m someArgument) `bind` \b ->
return $ (f a b)
在后面的 lambda 函数bind
中,我们知道这a
将是someFunction
并且b
将会是someArgument
——因为它就是这样bind
做的:它模拟从 Monad 上下文中提取一个值,并以该 Monad 独有的任何特殊处理为模。
所以最后一行真的变成了
return $ f someFunction someArgument
现在让我们退后一步,记住我们创建的目标是在Monad 上下文ap
中调用。因此,无论我们如何使用yield,它都必须是函数 application 的结果。someFunction
someArgument
return
someFunction someArgument
那么我们怎样才能使两个表达式相等
f someFunction someArgument ==? someFunction someArgument
好吧,如果我们让,x = (someFunction someArgument)
那么我们正在寻找一个f
这样的函数
f x = x
所以我们知道这f
需要id
。
回到开头,这意味着我们正在寻找liftM2 id
.
基本上说如果是一个可以操作的函数,liftM2 id ma mb
我将这样做,然后将“让他们独自一人”并让它做它的事情,同时在 Monad 上下文中返回结果。m (id a b)
a
b
id
a
b
这就像我们被迫liftM2
有旁观者的偏见。
为了解决这个问题,a
必须有一个从“TypeOfb”到“SomeReturnType”的函数类型,或者TypeOfb -> SomeReturnType
,因为b
是a
的预期参数。当然b
必须有TypeOfb
。
如果你允许我滥用符号,那么我们就随意使用符号“a”代表“TypeOfb”,使用符号“b”代表“SomeReturnType”:
`b` --> "a" is its type
`a` --> "a -> b" is its type
那么类型签名ap
将是
ap :: Monad m => m (TypeOfB -> SomeReturnType) -> m TypeOfB -> m SomeReturnType
==>
ap :: Monad m => m (a -> b) -> m a -> m b