我正在尝试为自己验证Haskell中的运算符和函数优先级。例如,以下代码
list = map foo $ xs
可以改写为
list = (map foo) $ (xs)
并最终将
list = map foo xs
我的问题曾经是,为什么第一个表述不会被重写为
list = (map foo $) xs
因为函数优先级总是高于运算符优先级,但我认为我找到了答案:运算符根本不允许作为函数的参数(当然,除非用括号括起来)。这是正确的吗?如果是这样的话,我觉得很奇怪,在RWH中没有提到这个机制/规则或者学习一个Haskell,或者我搜索过的任何其他地方。因此,如果您知道规则所在的地方,请链接到该地点。
- 编辑:感谢您的快速解答。我认为我的困惑来自于认为运算符字母会以某种方式评估某些东西,这可能会被函数作为参数使用。它帮助我记住,一个中缀运算符可以机械地转换为前缀函数。对第一个配方这样做会产生
($) (map foo) (xs)
毫无疑问,($)是消耗函数,并且由于两个公式是等价的,因此第一个公式中的$ literal不能被map使用。
答案 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]
。