最好的答案是绝对正确的,并且可以从单独的类型快速有效地工作。一旦你擅长Haskell(免责声明:我不是),那么这是理解这一点比效率函数定义更有效的方法。
但是由于我最近不得不在ap
这个问题上与ap
纠缠在一起,所以我决定分享我的经验,因为它可能会提供一些额外的直觉。首先,就像单体挑战问一样,我将使用名称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
选择了正确的函数,让我们暂时将函数f
单独保留一会儿,然后考虑如何将其作为ap
。
假设我们要传递一个函数值Monad作为上面的第一部分,即ma
部分。它可能类似于Just (+3)
或[(+3), (*2)]
- 一些生活在Monad环境中的“功能”。
而且我们提供一个参数值单子作为第二部分,所述mb
部分,如Just 5
或[6,7,8]
- 一些“值”生活在一个单子上下文,其可以用作用于活内部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
现在,让我们退后一步,记住,我们在创造ap
目标是调用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
说我该怎么办m (id a b)
所以如果a
是可以在b
操作功能,然后将id
“只是让他们独立”,让a
做的事情b
,而返回的内部结果Monad上下文。
这就像我们迫使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
也许拼写'AP = liftM2($)'会更照明。这意味着完全一样的东西。 – luqui 2011-03-19 01:30:04
@luqui:只为那些不知道的人,'($)'只是专用于功能的'id'。 – fuz 2011-03-19 10:56:00