在尝试更好地理解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 a1
转换为m (a -> b)
。我在这里缺少什么?
答案 0 :(得分:17)
来自a
的类型变量id
可以在任何类型实例化,在这种情况下,该类型为a -> b
。
所以我们在id
实例化(a -> b) -> (a -> b)
。现在,来自a1
的类型变量liftM2
正在(a -> b)
实例化,a2
正在a
实例化,而r
正在实例化b
。
总而言之,liftM2
已在((a -> b) -> (a -> b)) -> m (a -> b) -> m a -> m b
和liftM2 id :: m (a -> b) -> m a -> m b
实例化。
答案 1 :(得分:2)
最佳答案肯定是正确的,并且仅从类型中快速有效地工作。一旦你擅长Haskell(免责声明:我不是),那么这是理解这一点的一种更有效的方式,而不是通过功能定义。
但是,由于我最近在The Monad Challenges工作时不得不与ap
完全解决这个问题,我决定分享我的经验,因为它可能提供一些额外的直觉。
首先,正如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
或[6,7,8]
- 一些生活在Monad环境中的“价值”作为生活在ma
内的函数的参数。
然后我们有
liftM2 f (m someFunction) (m someArgument) =
(m someFunction) `bind` \a ->
(m someArgument) `bind` \b ->
return $ (f a b)
并且在bind
之后的lambda函数中,我们知道a
将是someFunction
而b
将是someArgument
- 因为这就是{ {1}}:它模拟Monad上下文中的值的提取,以模拟Monad特有的任何特殊处理。
所以最后一行确实变成了
bind
现在让我们退后一步,记住我们创建return $ f someFunction someArgument
的目标是在Monad上下文中的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
做事,同时将结果返回到Monad上下文中。
就像我们强迫b
有旁观者偏见。
为了解决这个问题,liftM2
必须有一个从“TypeOfb”到“SomeReturnType”或a
的函数类型,因为TypeOfb -> SomeReturnType
是{ {1}}预期的论点。当然b
必须有a
。
如果你允许我滥用表示法,那么任意让我们只使用符号“a”代表“TypeOfb”而符号“b”代表“SomeReturnType”:
b
然后TypeOfb
的类型签名将是
`b` --> "a" is its type
`a` --> "a -> b" is its type