最佳答案绝对是正确的,并且仅从类型就可以快速有效地工作。一旦你擅长 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 5or [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 的结果。someFunctionsomeArgumentreturnsomeFunction 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)abidab
这就像我们被迫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