13

在试图更好地理解 Applicative 的同时,我查看了 <*> 的定义,它往往被定义为 ap,而它又被定义为:

ap                :: (Monad m) => m (a -> b) -> m a -> m b
ap                =  liftM2 id

查看 liftM2 和 id 的类型签名,即:

liftM2  :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
id                      :: a -> a

我不明白如何仅通过传入 id,类型签名的相关部分似乎从 转换(a1 -> a2 -> r) -> m a1m (a -> b)。我在这里想念什么?

4

2 回答 2

17

类型变量afromid可以被实例化为任何类型,在这种情况下,该类型是a -> b.

所以我们id(a -> b) -> (a -> b). 现在,类型变量a1fromliftM2正在被实例化(a -> b)a2正在被实例化a,并且r正在被实例化b

将它们放在一起,在和liftM2处实例化。((a -> b) -> (a -> b)) -> m (a -> b) -> m a -> m bliftM2 id :: m (a -> b) -> m a -> m b

于 2011-03-19T00:05:48.343 回答
3

最佳答案绝对是正确的,并且仅从类型就可以快速有效地工作。一旦你擅长 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,因为ba的预期参数。当然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  
于 2016-01-29T04:38:21.390 回答