了解 Data.Function.on 类型签名
Understanding Data.Function.on type signature
在 Haskell 语法和函数式编程语言方面,我仍然是初学者,所以当我查看 Data.Function.on
的类型声明时,即 on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
,我的解释是它需要四个参数:(b -> b -> c)
、(a -> b)
、a
、a
和 returns c
。但是,当我查看 Data.Function.on
的一般使用语法 (*) `on` f = \x y -> f x * f y
时,它只采用两个函数参数,而不是四个,那么类型签名与使用语法有何关系?
my interpretation is that it takes four parameters
所有 Haskell 函数都有一个参数。其中一些只是 return 其他功能。
查看 on
签名的最佳方式是作为高阶函数:(b -> b -> c) -> (a -> b) -> (a -> a -> c)
。这表示 "if you give me a binary operator that takes b
s and gives a c
and a way to get b
s from a
s, I will give you a binary operator that takes a
s and gives a c
"。你可以在定义中看到这个:
(*) `on` f = \x y -> f x * f y
函数类型的 Haskell 箭头隐藏了一个简单但聪明的想法。您必须将 ->
视为运算符,如 +
和 -
,但用于类型。它采用两种类型作为参数,并为您提供一个由函数组成的新类型。所以在
Int -> String
你有类型 Int
和 String
,你得到一个从 Int 到 String 的函数。
就像任何其他运算符一样,您需要为它们的链条制定规则。如果你想到-
,这是什么意思?
10 - 6 - 4
是(10 - 6) - 4 = 0
,还是10 - (6 - 4) = 8
?答案是第一个,这就是为什么我们说-
是"left associative"。
->
运算符是右结合的,所以
foo :: Int -> String -> String
其实就是
foo :: Int -> (String -> String)
想想这意味着什么。这意味着 foo
不接受 2 个参数,而 return 是类型 String
的结果,它实际上接受 1 个参数(Int
)并且 return 是一个新函数采用第二个参数(String
)和 returns 最后的 String
.
函数应用程序以相同的方式工作,除了它是左关联的。所以
foo 15 "wibble"
其实就是
(foo 15) "wibble"
因此 foo
应用于 15
并且 return 是一个新函数,然后应用于 "wibble"
。
这导致了一个巧妙的技巧:当你调用一个函数时不必提供所有参数(就像你在几乎所有其他编程语言中所做的那样),你可以只提供第一个或前几个,并取回一个需要其余参数的新函数。
这就是 on
的情况。我将使用更具体的版本,其中 'f' 替换为 'length'。
(*) on
长度
你给 on
它的前两个参数。结果是一个需要其他两个的新函数。在类型中,
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
在本例中 (*)
的类型为 Num n => n -> n -> n
(我使用不同的字母来减少混淆),因此它与 [=34= 的第一个参数的类型相匹配],得出的结论是,如果类型 b
被 n
替代,那么类型 c
也必须是,并且也必须是一个 Num
实例。因此 length
必须 return 一些数字类型。碰巧 length
的类型是 [d] -> Int
,而 Int
是 Num
的一个实例,这样就可以了。所以最后你会得到:
(*) `on` length :: [d] -> [d] -> Int
作为直观的帮助,我将其理解为“如果你给我一个 类型 b
的比较器 ,以及一种提取类型 [=10] 的值的方法=] 从 a
类型的值,我会给你一个 comparator 类型 a
".
例如如果 a
是某种复合数据类型,而 b
是这些数据值的某些数值属性,则可以使用 Data.Function.on
.
表达对这些复合数据类型进行排序的想法
在 Haskell 语法和函数式编程语言方面,我仍然是初学者,所以当我查看 Data.Function.on
的类型声明时,即 on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
,我的解释是它需要四个参数:(b -> b -> c)
、(a -> b)
、a
、a
和 returns c
。但是,当我查看 Data.Function.on
的一般使用语法 (*) `on` f = \x y -> f x * f y
时,它只采用两个函数参数,而不是四个,那么类型签名与使用语法有何关系?
my interpretation is that it takes four parameters
所有 Haskell 函数都有一个参数。其中一些只是 return 其他功能。
查看 on
签名的最佳方式是作为高阶函数:(b -> b -> c) -> (a -> b) -> (a -> a -> c)
。这表示 "if you give me a binary operator that takes b
s and gives a c
and a way to get b
s from a
s, I will give you a binary operator that takes a
s and gives a c
"。你可以在定义中看到这个:
(*) `on` f = \x y -> f x * f y
函数类型的 Haskell 箭头隐藏了一个简单但聪明的想法。您必须将 ->
视为运算符,如 +
和 -
,但用于类型。它采用两种类型作为参数,并为您提供一个由函数组成的新类型。所以在
Int -> String
你有类型 Int
和 String
,你得到一个从 Int 到 String 的函数。
就像任何其他运算符一样,您需要为它们的链条制定规则。如果你想到-
,这是什么意思?
10 - 6 - 4
是(10 - 6) - 4 = 0
,还是10 - (6 - 4) = 8
?答案是第一个,这就是为什么我们说-
是"left associative"。
->
运算符是右结合的,所以
foo :: Int -> String -> String
其实就是
foo :: Int -> (String -> String)
想想这意味着什么。这意味着 foo
不接受 2 个参数,而 return 是类型 String
的结果,它实际上接受 1 个参数(Int
)并且 return 是一个新函数采用第二个参数(String
)和 returns 最后的 String
.
函数应用程序以相同的方式工作,除了它是左关联的。所以
foo 15 "wibble"
其实就是
(foo 15) "wibble"
因此 foo
应用于 15
并且 return 是一个新函数,然后应用于 "wibble"
。
这导致了一个巧妙的技巧:当你调用一个函数时不必提供所有参数(就像你在几乎所有其他编程语言中所做的那样),你可以只提供第一个或前几个,并取回一个需要其余参数的新函数。
这就是 on
的情况。我将使用更具体的版本,其中 'f' 替换为 'length'。
(*) on
长度
你给 on
它的前两个参数。结果是一个需要其他两个的新函数。在类型中,
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
在本例中 (*)
的类型为 Num n => n -> n -> n
(我使用不同的字母来减少混淆),因此它与 [=34= 的第一个参数的类型相匹配],得出的结论是,如果类型 b
被 n
替代,那么类型 c
也必须是,并且也必须是一个 Num
实例。因此 length
必须 return 一些数字类型。碰巧 length
的类型是 [d] -> Int
,而 Int
是 Num
的一个实例,这样就可以了。所以最后你会得到:
(*) `on` length :: [d] -> [d] -> Int
作为直观的帮助,我将其理解为“如果你给我一个 类型 b
的比较器 ,以及一种提取类型 [=10] 的值的方法=] 从 a
类型的值,我会给你一个 comparator 类型 a
".
例如如果 a
是某种复合数据类型,而 b
是这些数据值的某些数值属性,则可以使用 Data.Function.on
.