哈斯克尔:翻动咖喱美元运营商

Ami*_*irW 12 haskell

假设我定义了这个函数:

f = ($ 5)
Run Code Online (Sandbox Code Playgroud)

然后我可以申请它:

> f (\x -> x ^ 2)
25
Run Code Online (Sandbox Code Playgroud)

它的类型是:

:t f
f :: (Integer -> b) -> b
Run Code Online (Sandbox Code Playgroud)

这是有道理的,它将一个函数作为参数,并返回应用于该函数的函数Integer 5.

现在我定义这个函数:

g = flip f
Run Code Online (Sandbox Code Playgroud)

我希望这没有意义,因为它f是单个参数的函数.

但是,检查其类型:

:t g
g :: b -> (Integer -> b -> c) -> c
Run Code Online (Sandbox Code Playgroud)

所以现在g是2个参数的函数!

将它应用于某些值:

> g [2, 4, 6] (\x y -> x:y)
[5,2,4,6]
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?什么是flip ($ 5)真正的意思?

bhe*_*ilr 9

遵循以下类型:

($ 5) :: (Int -> a) -> a
flip  :: (x -> y -> z) -> y -> x -> z
Run Code Online (Sandbox Code Playgroud)

但由于->是右结合,类型x -> y -> z相当于x -> (y -> z),所以

flip  :: (x         -> (y -> z)) -> y -> x -> z
($ 5) :: (Int -> a) -> a
Run Code Online (Sandbox Code Playgroud)

所以x ~ (Int -> a)(y -> z) ~ a,所以代回:

($ 5) :: (Int -> (y -> z)) -> (y -> z)
Run Code Online (Sandbox Code Playgroud)

并简化

($ 5) :: (Int -> y -> z) -> y -> z
Run Code Online (Sandbox Code Playgroud)

所以

flip ($ 5) :: y -> (Int -> y -> z) -> z
Run Code Online (Sandbox Code Playgroud)

这相当于你看到的类型(虽然我用Int而不是Integer保存打字).

这就是说,($ 5)当传递给flip需要2个参数的函数时,获取专门的类型.拥有像($ 5) const,在哪里const :: a -> b -> a和那样的东西是完全有效的($ 5) const :: b -> Int.所有($ 5)这一切都是5作为函数参数应用,不一定是函数参数.这是部分应用程序的示例,其中并非所有参数都提供给函数.这就是为什么你可以做的事情map (subtract 1) [1, 2, 3].

如何使用的一个例子flip ($ 5)是:

> flip ($ 5) 2 (**)
25.0
> flip ($ 5) 1 (-)
4.0
> let f x y = (x, y)
> flip ($ 5) 1 f
(5, 1)
Run Code Online (Sandbox Code Playgroud)


chi*_*chi 5

混淆源于多态函数的"参数数量"的松散概念.例如,很有可能这样说

f :: (Integer -> b) -> b
Run Code Online (Sandbox Code Playgroud)

有一个参数(一个函数).然而,更精确的说法f是具有至少一个参数的函数.这是因为b由于多态性,类型变量可以用任何类型替换,从而产生例如

f :: (Integer -> String) -> String
f :: (Integer -> Double) -> Double
...
Run Code Online (Sandbox Code Playgroud)

这些确实是具有一个参数的函数,但也适用于例如

f :: (Integer -> (String -> Double)) -> (String -> Double)
Run Code Online (Sandbox Code Playgroud)

哪里b已被功能类型取代String -> Double.这种替换使得第二个参数以一种看似神奇的方式"出现":在返回a之前,f可以采用第一个参数(二元函数Integer -> String -> Double),然后是第二个参数(a String)Double.

请注意,当多态类型以... -> b某种类型变量结束时,总会出现此现象b.

让我以琐事结束:"许多"论证如何具有身份功能id?嗯,直觉上我会说一个,但让我检查......

> id (+) 3 4
7
> id id id id id (+) 3 4
7
Run Code Online (Sandbox Code Playgroud)

......也许很多人都是更好的答案.