`ap zip tail` 表达式是如何工作的
Posted
技术标签:
【中文标题】`ap zip tail` 表达式是如何工作的【英文标题】:How does the expression `ap zip tail` work 【发布时间】:2013-10-11 11:49:45 【问题描述】:我想知道如何写f x = zip x (tail x)
in point free。所以我使用了pointfree程序,结果是f = ap zip tail
。 ap
是 Control.Monad 中的一个函数
我不明白无点定义是如何工作的。我希望我能从类型的角度理解它。
import Control.Monad (ap)
let f = ap zip tail
let g = ap zip
:info ap zip tail f g
ap :: Monad m => m (a -> b) -> m a -> m b
-- Defined in `Control.Monad'
zip :: [a] -> [b] -> [(a, b)] -- Defined in `GHC.List'
tail :: [a] -> [a] -- Defined in `GHC.List'
f :: [b] -> [(b, b)] -- Defined at <interactive>:3:5
g :: ([a] -> [b]) -> [a] -> [(a, b)]
-- Defined at <interactive>:4:5
通过查看表达式ap zip tail
,我认为zip 是ap
的第一个参数,而tail 是ap
的第二个参数。
Monad m => m (a -> b) -> m a -> m b
\--------/ \---/
zip tail
但这是不可能的,因为zip
和tail
的类型与ap
函数所需要的完全不同。即使考虑到列表是某种单子。
【问题讨论】:
我唯一能想到的就是ap类型中的a
变成了zip类型的[a] -> [b]
。如果是这样,那么一般来说,统一(如果它是正确的词)规则是什么?
ghci 说类型是 ap zip tail :: Monad ((->) [b]) => [b] -> [(b, b)]
... 没有太多调查,我会说 Monad ((->) [b])
是您想要阅读的内容。我认为 learnyouahaskell.com/for-a-few-monads-more#reader 可能会帮助您了解这里发生了什么。
顺便提一下,zip <*> tail
是等效的并且看起来更漂亮
@jozefg 对我来说最漂亮的是原始的f x = zip x (tail x)
可能是因为它是唯一能让我立即看到作者意图的形式。
【参考方案1】:
所以ap
的类型签名是Monad m => m (a -> b) -> m a -> m b
。你已经给它zip
和tail
作为参数,所以让我们看看它们的类型签名。
从tail :: [a] -> [a] ~ (->) [a] [a]
开始(这里~
是类型的相等运算符),如果我们将此类型与ap
的第二个参数的类型进行比较,
(->) [x] [x] ~ m a
((->) [x]) [x] ~ m a
我们得到a ~ [x]
和m ~ ((->) [x]) ~ ((->) a)
。我们已经可以看到我们所在的 monad 是 (->) [x]
,而不是 []
。如果我们将我们能做的代入ap
的类型签名,我们会得到:
(((->) [x]) ([x] -> b)) -> (((->) [x]) [x]) -> (((->) [x]) b)
由于这不是很可读,所以通常可以写成
([x] -> ([x] -> b)) -> ([x] -> [x]) -> ([x] -> b)
~ ([x] -> [x] -> b ) -> ([x] -> [x]) -> ([x] -> b)
zip
的类型是[x] -> [y] -> [(x, y)]
。我们已经可以看到这与ap
where 的第一个参数一致
[x] ~ [x]
[y] ~ [x]
[(x, y)] ~ b
这里我垂直列出了类型,以便您可以轻松查看哪些类型排列。所以很明显x ~ x
、y ~ x
和[(x, y)] ~ [(x, x)] ~ b
,所以我们可以完成将b ~ [(x, x)]
代入ap
的类型签名并得到
([x] -> [x] -> [(x, x)]) -> ([x] -> [x]) -> ([x] -> [(x, x)])
-- zip tail ( ap zip tail )
-- ap zip tail u = zip u (tail u)
我希望这能为你解决问题。
编辑:作为 cmets 中的 danvari pointed out,monad (->) a
有时也称为 reader monad。
【讨论】:
可能值得一提的是 (->) 是 Reader-monad【参考方案2】:理解这个有两个方面:
-
类型魔法
实现的信息流
首先,这帮助我理解了类型魔法:
1) zip : [a] → ( [a] → [(a,a)] )
2) tail : [a] → [a]
3) zip <*> tail : [a] → [(a,a)]
4) <*> : Applicative f ⇒ f (p → q) → f p → f q
在这种情况下,对于<*>
,
5) f x = y → x
注意,在 5 中,f
是一个类型构造函数。将f
应用于x
会生成一个类型。此外,这里=
被重载以表示类型等价。
y
当前是占位符,在本例中为[a]
,表示
6) f x = [a] -> x
使用 6,我们可以将 1,2 和 3 改写如下:
7) zip : f ([a] → [(a,a)])
8) tail : f [a]
9) zip <*> tail : f ([a] → [(a,a)]) → f [a] → f [(a,a)]
所以,看 4,我们代入如下:
10) p = [a]
11) q = [(a,a)]
12) f x = [a] → x
(此处重复 6 为 12)
其次,信息流,即实际功能。这样比较容易,从Applicative instance of y →
的<*>
的定义就很清楚了,这里改写成不同的标识符名称,使用中缀样式:
13) g <*> h $ xs = g xs (h xs)
代入如下:
14) g = zip
15) h = tail
给予:
zip <*> tail $ xs (Using 14 and 15)
==
zip xs (tail xs) (Using 13 )
【讨论】:
我仍然不清楚的部分是您如何获得第 5 步(这似乎是 ghc 自动推断我们想要使用 (->))。它可以自动执行此操作,因为它是唯一有效的应用程序实例吗?以上是关于`ap zip tail` 表达式是如何工作的的主要内容,如果未能解决你的问题,请参考以下文章