(a -> a) 和 a -> a 的区别

5
我注意到(尽管有人告诉过我(a->a)a->a意思相同),但当我使用(a->a)时会出现错误消息。那么,在类型之间使用括号时,我应该仅使用(a->a)吗?(例如(5 + 3)而不是5 + 3)?我不确定何时需要使用它。

你能分享一下错误吗? - Josh Lee
这也很令人困惑,因为您给出的标题看起来像是类型中的'->'操作符,而您的示例却是表达式(例如5+3)。 - Don Stewart
5个回答

14

(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


4
括号在Haskell中消除了一些歧义,当编译器没有其他可用信息时:
应用程序从左侧关联,
因此您可以省略函数参数的括号。
涉及中缀运算符的表达式由运算符的结合性消除歧义。
因此,在许多具有不同结合性的二元运算符中,无需使用括号。
必须同时将具有相同优先级的连续未加括号的运算符设置为左或右关联,以避免语法错误。
最后:
给定一个未加括号的表达式x op y op z,必须在x op y或y op z周围添加括号,除非满足有关优先级的某些条件
如果上述陈述毫无意义,请遵循我的一般建议:过度使用括号,直到您学会规则。或者,认真研究 Haskell报告

2
考虑表达式10 / 2 / 5。这是否与(10 / 2) / 510 / (2 / 5)相同?如果您将/解释为数学除法,则前者是正确的,而后者是错误的。因此,你看,你的问题的答案是“有差异,但只有在某些情况下”。
类型则相反。a -> b -> ca -> (b -> c)相同,并且绝对不同于(a -> b) -> c
你说你不太确定何时需要:那就是了。当您的函数参数也是一个函数时,就有必要了。
考虑map :: (a -> b) -> [a] -> [b]。这不同于a -> b -> [a] -> [b],因为(a -> b)表示特定类型的函数:从类型a到类型b的函数。 iterate :: (a -> a) -> a -> [a]则更有趣。该函数要求第一个参数中函数的输入和输出类型必须相同。
您可能会对柯里化和部分应用程序感兴趣。 许多资源中的一个好资源:Learn you a Haskell #Curried Functions

1

这只有在编写高阶函数时才会有所区别。例如:

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

()在类型转换中的规则与普通表达式基本相同。它就像是高一级的表达式。


0

它们是一样的。你能描述一下当你使用(a -> a)时出现的错误吗?我在ghci-7.0.3上使用它没有问题:

Prelude> let f :: (a -> a); f = id
Prelude> f "foo"
"foo"
Prelude>

通常情况下,当您将函数用作参数时,需要在类型中使用括号。例如,map :: (a -> b) -> [a] -> [b]。如果没有括号,它会意味着其他的东西。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接