繁体   English   中英

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

[英]Haskell: Flipping a curried dollar operator

假设我定义了这个函数:

f = ($ 5)

然后我可以申请它:

> f (\x -> x ^ 2)
25

它的类型是:

:t f
f :: (Integer -> b) -> b

这是有道理的,它将函数作为参数,并返回应用于Integer 5此函数。

现在我定义这个函数:

g = flip f

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

但是,检查其类型:

:t g
g :: b -> (Integer -> b -> c) -> c

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

将它应用于某些值:

> g [2, 4, 6] (\x y -> x:y)
[5,2,4,6]

这里发生了什么? flip ($ 5)究竟意味着什么?

遵循以下类型:

($ 5) :: (Int -> a) -> a
flip  :: (x -> y -> z) -> y -> x -> z

但由于->是右关联的,类型x -> y -> z相当于x -> (y -> z) ,所以

flip  :: (x         -> (y -> z)) -> y -> x -> z
($ 5) :: (Int -> a) -> a

所以x ~ (Int -> a)(y -> z) ~ a ,所以替换回来:

($ 5) :: (Int -> (y -> z)) -> (y -> z)

并简化

($ 5) :: (Int -> y -> z) -> y -> z

所以

flip ($ 5) :: y -> (Int -> y -> z) -> z

这相当于你所看到的类型(尽管我使用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)

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

f :: (Integer -> b) -> b

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

f :: (Integer -> String) -> String
f :: (Integer -> Double) -> Double
...

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

f :: (Integer -> (String -> Double)) -> (String -> Double)

其中b已被函数类型String -> Double替换。 这种替换使得第二个参数以一种看似神奇的方式“出现”: f可以在返回Double之前获取第一个参数(二进制函数Integer -> String -> Double ),然后是第二个参数( String )。

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

让我以一个琐事结束:“很多”参数如何具有身份功能id 嗯,直觉上我会说一个 ,但让我检查......

> id (+) 3 4
7
> id id id id id (+) 3 4
7

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

函数flip翻转参数的顺序,所以这些都是相同的:

f (\x y -> x:y) [2, 4, 6]

[5,2,4,6]

flip f  [2, 4, 6] (\x y -> x:y)

[5,2,4,6]

g [2, 4, 6] (\x y -> x:y)

[5,2,4,6]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM