Haskell运算符与函数优先级

时间:2010-06-26 20:24:40

标签: function haskell operator-keyword operator-precedence

我正在尝试为自己验证Haskell中的运算符和函数优先级。例如,以下代码

list = map foo $ xs

可以改写为

list = (map foo) $ (xs)

并最终将

list = map foo xs

我的问题曾经是,为什么第一个表述不会被重写为

list = (map foo $) xs

因为函数优先级总是高于运算符优先级,但我认为我找到了答案:运算符根本不允许作为函数的参数(当然,除非用括号括起来)。这是正确的吗?如果是这样的话,我觉得很奇怪,在RWH中没有提到这个机制/规则或者学习一个Haskell,或者我搜索过的任何其他地方。因此,如果您知道规则所在的地方,请链接到该地点。

- 编辑:感谢您的快速解答。我认为我的困惑来自于认为运算符字母会以某种方式评估某些东西,这可能会被函数作为参数使用。它帮助我记住,一个中缀运算符可以机械地转换为前缀函数。对第一个配方这样做会产生

($) (map foo) (xs)

毫无疑问,($)是消耗函数,并且由于两个公式是等价的,因此第一个公式中的$ literal不能被map使用。

5 个答案:

答案 0 :(得分:35)

首先,应用程序(空格)是最高优先级“运算符”。

其次,在Haskell中,运算符和函数之间并没有区别,除了默认情况下运算符是中缀,而函数不是。您可以使用反引号将函数转换为中缀

2 `f` x

并将运算符转换为带有parens的前缀:

(+) 2 3

所以,你的问题有点困惑。

现在,特定的函数和运算符将声明优先级,您可以在GHCi中找到“:info”:

Prelude> :info ($)
($) :: (a -> b) -> a -> b   -- Defined in GHC.Base

infixr 0 $

Prelude> :info (+)

class (Eq a, Show a) => Num a where
  (+) :: a -> a -> a

infixl 6 +

显示优先级和关联性。

答案 1 :(得分:25)

你是对的。此规则是Haskell Report定义的Haskell语法的一部分。特别注意在第3节“表达式”中,函数应用程序的参数(fexp)必须是aexp。 aexp允许运算符作为节的一部分,也可以在带括号的表达式中,但不允许运算符。

map foo $ xs中,Haskell语法意味着将其解析为应用于二元运算符$的两个表达式。正如sepp2k所说,语法(map foo $)是左侧部分,具有不同的含义。

我必须承认我从来没有考虑过这个问题,实际上必须在报告中查一查,看看为什么运营商会采取行动。

答案 2 :(得分:15)

除了已经由其他答案提供的信息之外,请注意不同的运算符可以具有与其他运算符不同的优先级,以及左/右或非关联。 您可以在Haskell 98 Report fixity section中找到Prelude运算符的这些属性。

+--------+----------------------+-----------------------+-------------------+
| Prec-  |   Left associative   |    Non-associative    | Right associative |
| edence |      operators       |       operators       |    operators      |
+--------+----------------------+-----------------------+-------------------+
| 9      | !!                   |                       | .                 |
| 8      |                      |                       | ^, ^^, **         |
| 7      | *, /, `div`,         |                       |                   |
|        | `mod`, `rem`, `quot` |                       |                   |
| 6      | +, -                 |                       |                   |
| 5      |                      |                       | :, ++             |
| 4      |                      | ==, /=, <, <=, >, >=, |                   |
|        |                      | `elem`, `notElem`     |                   |
| 3      |                      |                       | &&                |
| 2      |                      |                       | ||                |
| 1      | >>, >>=              |                       |                   |
| 0      |                      |                       | $, $!, `seq`      |
+--------+----------------------+-----------------------+-------------------+
  

任何缺少固定性声明的运算符都假定左关联,优先级为9

请记住,函数应用程序具有最高优先级(考虑优先级10与表中的其他优先级相比)[1]

答案 3 :(得分:11)

区别在于中缀运算符放在它们的参数之间,所以这个

list = map foo $ xs

可以用前缀形式重写为

list = ($) (map foo) xs

,根据$运算符的定义,只是

list = (map foo) xs

答案 4 :(得分:10)

如果用括号括起运算符(即map foo ($) xs,它们确实将作为(map foo ($)) xs传递),则可以将运算符作为函数参数传递。但是,如果不用括号括起它们,那么它们是正确的,它们不能作为参数传递(或分配给变量)。

另请注意,语法(someValue $)(其中$可以是任何运算符)实际上意味着不同的东西:它等同于\x -> someValue $ x,即它部分地将运算符应用于其左操作数(在$的情况下,当然是noop)。同样,($ x)将运算符部分应用于右操作数。因此map ($ x) [f, g, h]会评估为[f x, g x, h x]