了解 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)aa 和 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 bs and gives a c and a way to get bs from as, I will give you a binary operator that takes as and gives a c"。你可以在定义中看到这个:

(*) `on` f = \x y -> f x * f y

函数类型的 Haskell 箭头隐藏了一个简单但聪明的想法。您必须将 -> 视为运算符,如 +-,但用于类型。它采用两种类型作为参数,并为您提供一个由函数组成的新类型。所以在

Int -> String

你有类型 IntString,你得到一个从 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= 的第一个参数的类型相匹配],得出的结论是,如果类型 bn 替代,那么类型 c 也必须是,并且也必须是一个 Num 实例。因此 length 必须 return 一些数字类型。碰巧 length 的类型是 [d] -> Int,而 IntNum 的一个实例,这样就可以了。所以最后你会得到:

(*) `on` length :: [d] -> [d] -> Int

作为直观的帮助,我将其理解为“如果你给我一个 类型 b 的比较器 ,以及一种提取类型 [=10] 的值的方法=] 从 a 类型的值,我会给你一个 comparator 类型 a".

例如如果 a 是某种复合数据类型,而 b 是这些数据值的某些数值属性,则可以使用 Data.Function.on.

表达对这些复合数据类型进行排序的想法