我注意到(尽管我曾经被告知(a -> a)
并且a -> a
意思相同),当我使用(a -> a)
. 我应该只(a -> a)
在类型之间使用括号时使用吗?(即(5 + 3)
代替5 + 3
)?只是不太确定何时需要
5 回答
(a -> a)
和单独a -> a
一样,
ff :: (a -> a) -- this compiles
ff = id
gg :: a -> a
gg = id
h :: a -> a -> Bool
h _ _ = True
i = h ff gg -- this compiles => ff & gg are of the same type.
但与更多类型结合时会有所不同,例如:
a -> a -> b
(a -> a) -> b
这是因为->
是右关联的,所以a -> a -> b
实际上是指a -> (a -> b)
(取a
一个函数并返回一个函数),这与(a -> a) -> b
(取一个函数并返回一个函数)不同b
。
这就像是(1+2)*3
不同的1+2*3
。
当编译器可用的其他信息无济于事时,括号消除了 Haskell 中的几个结构的歧义:
左侧的应用程序关联
所以你可以省略函数参数的括号。
涉及中缀运算符的表达式通过运算符的固定性消除歧义。
所以在许多情况下不需要使用具有不同固定性的二元运算符的括号。
具有相同优先级的连续无括号运算符必须都是左关联或右关联以避免语法错误。
最后:
给定一个不带括号的表达式
x op y op z
,必须在x op y
or周围加上括号y op z
,除非关于优先级的某些条件成立。
如果上述陈述没有任何意义,我的一般建议是:过度使用括号,直到您了解规则。或者,非常努力地研究Haskell 报告。
考虑表达式10 / 2 / 5
。和(10 / 2) / 5
or一样10 / (2 / 5)
吗?如果解释/
为数学除法,则前者为真,后者为假。所以你看,你的问题的答案是“有区别,但只是有时”。
类型则相反。a -> b -> c
和 一样a -> (b -> c)
,而且绝对不一样(a -> b) -> c
。
你说你不太确定什么时候需要:嗯,就是这样。当你的函数的参数也是一个函数时,那么它是必要的。
考虑map :: (a -> b) -> [a] -> [b]
。这与a -> b -> [a] -> [b]
你看到的不同,它(a -> b)
表示一种特定类型的函数:从 typea
到 type的函数b
。
iterate :: (a -> a) -> a -> [a]
更有趣。该函数要求第一个参数中函数的输入输出类型必须相同。
您可能有兴趣阅读 currying 和部分应用程序。许多资源中的一个很好的资源:Learn you a Haskell # Curried Functions
这仅在您制作高阶函数时才会有所不同。例如:
f :: a -> a -> b
是一个函数,它需要两个类型的参数a
并返回一个类型的值b
,就像这样
f 2 2
f True True
但是功能
f :: (a -> a) -> b
期望一个函数作为参数。如果它们是类型推断中的唯一参数,那么它们唯一的时间是相同的a -> a
,就像这里(a -> a)
f :: (a -> a)
-- Same type as
f :: a -> a
in 类型的规则()
与普通表达式中的规则几乎相同。这就像一个级别的表达式。
他们是一样的。你能描述一下你使用时遇到的错误(a -> a)
吗?使用 ghci-7.0.3 对我来说效果很好:
Prelude> let f :: (a -> a); f = id
Prelude> f "foo"
"foo"
Prelude>
通常,当您使用函数作为参数时,您需要在类型中使用括号。例如,map :: (a -> b) -> [a] -> [b]
。没有括号,这将意味着其他东西。