当谈到Haskell语法和函数式编程语言时,我仍然是初学者,所以当我查看Data.Function.on
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
的类型声明时,我的解释是需要四个参数:(b -> b -> c)
,(a -> b)
,a
,a
,并返回c
。但是,当我查看Data.Function.on
的通用语法(*) `on` f = \x y -> f x * f y
时,它只接受两个函数参数,而不是四个,那么类型签名与使用语法有何关系?
答案 0 :(得分:3)
我的解释是它需要四个参数
所有Haskell函数都有一个参数。其中一些只是返回其他功能。
查看on
签名的最佳方式是作为高阶函数:(b -> b -> c) -> (a -> b) -> (a -> a -> c)
。这说'#34;如果你给我一个二元运算符,它需要b
s并给出c
以及从b
获得a
s的方法,我会给你一个二元运算符,它接受a
s并给出c
"。您可以在定义中看到这一点:
(*) `on` f = \x y -> f x * f y
答案 1 :(得分:1)
函数类型的Haskell箭头隐藏了一个简单但聪明的想法。您必须将->
视为运算符,例如+
和-
,但对于类型。它有两种类型作为参数,并为您提供一个由函数组成的新类型。所以在
Int -> String
您有类型Int
和String
,并且您可以获得从Int到String的函数。
就像任何其他运算符一样,您需要一个规则来链接它们。如果你想到-
,这是什么意思?
10 - 6 - 4
是(10 - 6) - 4 = 0
还是10 - (6 - 4) = 8
?答案是第一个,这就是为什么我们说-
是"左关联"。
->
运算符是右关联的,所以
foo :: Int -> String -> String
实际上意味着
foo :: Int -> (String -> String)
想想这意味着什么。这意味着foo
不会接受2个参数并返回类型String
的结果,它实际上需要1个参数(Int
)并返回一个新的函数,它接受第二个参数(String
)并返回最终String
。
函数应用程序的工作方式相同,只是左关联。所以
foo 15" wibble"
实际上意味着
(foo 15)" wibble"
因此foo
已应用于15
并返回一个新函数,然后将其应用于"wibble"
。
这导致了一个巧妙的技巧:在调用函数时不必提供所有参数(就像在其他几乎所有编程语言中一样),你可以只提供第一个或前几个,并得到返回一个需要其余参数的新函数。
这是on
发生的事情。我将使用更具体的版本,其中包括' f'被' length'取代。
(*)on
长度
你给on
前两个参数。结果是一个新功能,期望其他两个。在类型中,
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
在这种情况下,(*)
的类型为Num n => n -> n -> n
(我使用不同的字母来减少混淆),因此与{{1}的第一个参数的类型相匹配},得出的结论是,如果on
替换了类型b
,那么类型n
必须也是,并且也必须是c
个实例。因此Num
必须返回一些数字类型。碰巧的是,length
的类型为length
,而[d] -> Int
是Int
的实例,因此可以解决问题。所以在最后你会得到:
Num
答案 2 :(得分:0)
作为一种直观的帮助,我将其理解为“如果您给我一个b
类型的比较器,并且是一种从的值中提取b
类型的值的方法类型a
,我会给你一个类型为a
的比较器。
例如如果a
是某种复合数据类型,而b
是这些数据值的某些数字属性,则可以表达使用Data.Function.on
对这些复合数据类型进行排序的想法。