根据https://wiki.haskell.org/Compose的撰写类型,可以写为
compose :: [a -> a] -> (a -> a)
或compose :: [a -> a] -> a -> a
我认为这两种类型是不同的:前者采用函数列表并返回一个函数,后者采用函数列表和参数,然后最终返回一个值。
即,当函数(高阶函数)将另一个函数作为参数或作为结果返回函数时,参数(结果)周围的括号不应该被省略,例如,如果{{ 1}}删除括号,其含义会改变。
我是对还是错?
答案 0 :(得分:7)
这些都是相同的:
compose1 :: [a -> a] -> (a -> a)
compose1 [] = \x -> x
compose1 [f] = \x -> f x
compose1 (f1:f2:fs) = compose1 ((f2 . f1):fs)
compose2 :: [a -> a] -> a -> a
compose2 [] x = x
compose2 [f] x = f x
compose2 (f1:f2:fs) x = compose2 ((f2 . f1):fs) x
注意这些定义实际上是如何相同的,除了lambda从=
的一侧移动到另一侧。实际上,您始终可以执行以下转换:
f x y z = <expr x y z>
f x y = \z -> <expr x y z>
f x = \y -> \z -> <expr x y z> = \y z -> <expr x y z>
f = \x -> \y -> \z -> <expr x y z> = \x y z -> <expr x y z>
这实际上是编译器对所有函数的作用。如果使用-ddump-simpl
进行编译,您将看到转储的核心代码,其中所有函数都是根据lambdas定义的。这是因为Haskell使用的法则
f x = <expr>
相当于
f = \x -> <expr>
lambda语法可以被认为是比使用显式参数定义函数更基本的语法。
即,当函数(高阶函数)将另一个函数作为参数或作为结果返回函数时,不应省略参数(结果)周围的括号,例如{{1}删除括号,其含义将改变。
您认为无法从filter :: (a -> Bool) -> [a] -> [a]
的类型签名中删除括号,这是正确的,这是因为函数应用程序只是右关联。这意味着以下签名是等效的:
filter
这是通过关联到右边的意思,在添加嵌套括号时从右到左。但是,f :: a -> b -> c -> d -> e
f :: a -> (b -> (c -> (d -> e)))
的签名 等同于
f
仅仅因为-- Not equivalent!
f :: (((a -> b) -> c) -> d) -> e
不是完全关联的运算符。例如,->
你有
+
但是x + (y + z) = (x + y) + z
你有
->
这类似于x -> (y -> z) /= (x -> y) -> z
运算符,例如
:
最后一个表达式不会键入检查,因为1:2:3:4:[] == 1:(2:(3:(4:[])))
/= (((1:2):3):4):[]
生病,1:2
不是列表!
答案 1 :(得分:7)
箭头是右关联的,因此[a -> a] -> a -> a
和[a -> a] -> (a -> a)
是等效的。
如果删除filter
的类型签名中的括号,则会得到:
a -> Bool -> [a] -> [a]
a -> Bool -> ([a] -> [a])
a -> (Bool -> ([a] -> [a]))
...这与正确的类型签名不同。