`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 tailap 是 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

但这是不可能的,因为ziptail的类型与ap函数所需要的完全不同。即使考虑到列表是某种单子。

【问题讨论】:

我唯一能想到的就是ap类型中的a变成了zip类型的[a] -&gt; [b]。如果是这样,那么一般来说,统一(如果它是正确的词)规则是什么? ghci 说类型是 ap zip tail :: Monad ((-&gt;) [b]) =&gt; [b] -&gt; [(b, b)]... 没有太多调查,我会说 Monad ((-&gt;) [b]) 是您想要阅读的内容。我认为 learnyouahaskell.com/for-a-few-monads-more#reader 可能会帮助您了解这里发生了什么。 顺便提一下,zip &lt;*&gt; tail 是等效的并且看起来更漂亮 @jozefg 对我来说最漂亮的是原始的f x = zip x (tail x) 可能是因为它是唯一能让我立即看到作者意图的形式。 【参考方案1】:

所以ap 的类型签名是Monad m =&gt; m (a -&gt; b) -&gt; m a -&gt; m b。你已经给它ziptail 作为参数,所以让我们看看它们的类型签名。

tail :: [a] -&gt; [a] ~ (-&gt;) [a] [a] 开始(这里~ 是类型的相等运算符),如果我们将此类型与ap 的第二个参数的类型进行比较,

 (->) [x]  [x] ~ m a
((->) [x]) [x] ~ m a

我们得到a ~ [x]m ~ ((-&gt;) [x]) ~ ((-&gt;) a)。我们已经可以看到我们所在的 monad 是 (-&gt;) [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] -&gt; [y] -&gt; [(x, y)]。我们已经可以看到这与ap where 的第一个参数一致

[x]         ~    [x]   
[y]         ~    [x]   
[(x, y)]    ~    b

这里我垂直列出了类型,以便您可以轻松查看哪些类型排列。所以很明显x ~ xy ~ 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 (-&gt;) 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

在这种情况下,对于&lt;*&gt;

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 →&lt;*&gt;的定义就很清楚了,这里改写成不同的标识符名称,使用中缀样式:

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` 表达式是如何工作的的主要内容,如果未能解决你的问题,请参考以下文章

如何在计算表达式中定义自定义运算符

与私有变量相关的非法表达式开头

什么是最终的邮政编码和 zip 正则表达式?

Java--关于计算表达式中赋值的细节问题

美国和其他格式 zip 的正则表达式 [重复]

python 内置函数zip,map,三元,lambda表达式