[英]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.