如何发现GHCI中函数的优先级和关联性?

时间:2014-05-10 21:29:13

标签: haskell operators ghc ghci operator-precedence

有没有一种快速简便的方法来发现GHCI中的函数的优先级和关联性?

我发现一种直接的方法是将一个操作符与另一个操作符强制组合,直到获得优先级解析错误(PPE)。例如,要发现!!的优先级/关联性,我们可以为infix n定义10个0 ≤ n < 10的不同运算符:

>>> let infix 0 %; (%) = ($)
>>> undefined % undefined !! undefined
*** Exception: Prelude.undefined
>>> let infix 1 %; (%) = ($)
>>> undefined % undefined !! undefined
*** Exception: Prelude.undefined
[...]
>>> let infix 8 %; (%) = ($)
>>> undefined % undefined !! undefined
*** Exception: Prelude.undefined
>>> let infix 9 %; (%) = ($)
>>> undefined % undefined !! undefined

<interactive>:21:1:
    Precedence parsing error
        cannot mix `%' [infix 9] and `!!' [infixl 9] in the same infix expression

错误告诉您什么是先天性和固定性。由于定义的10个函数是非关联的,因此当优先级相等时,不可能避免PPE。天真的方法是尝试每个关联性的10个函数,给出一个20的WCET(因为如果你开始与目标相同的关联性,那个具有该关联性的10将会通过,但是一旦你到达那个失败就会发生下一个关联性的kth优先级,其中k是目标函数的优先级。)

但有更快的方法吗?我认为你可以做二分,平均实现log(10)步骤。例如,如果目标是!!!,其定义为!!,但infixl 6,我们可以制作表达式(:[]) % "AB" !!! 1,其中%是其中之一相同的10个虚拟功能。如果%的优先级太低而不能导致PPE,则表达式将产生"B"(因为表达式将解析为(:[]) % ("AB" !!! 1)),而如果优先级太高,它将yield (因为表达式将解析为((:[]) % "AB") !!! 1)。例如:

>>> let infixl 6 !!!; (!!!) = (!!)
>>> let infix 5 %; (%) = ($)
>>> (:[]) % "AB" !!! 1
"B"
>>> --too low
>>> let infix 7 %; (%) = ($)
>>> (:[]) % "AB" !!! 1
"*** Exception: Prelude.(!!): index too large

>>> --too high
>>> let infix 6 %; (%) = ($)
>>> (:[]) % "AB" !!! 1

<interactive>:10:1:
    Precedence parsing error
        cannot mix `%' [infix 6] and `!!!' [infixl 6] in the same infix expression
>>> --got it

但是,我不确定这种方法。

  • 你必须保持接下来猜测哪个数字的心理状态,并在脑海中进行分割等(通过纸张/计算器可能比直接方法中的10个步骤慢)。
  • 完全一般吗?你能不能总是像这样构建一个测试表达式?
  • 我不确定我是否总能比这更快地提出这样的表达方式。在这种情况下它确实更快,我实际上提出了二分法之前我提出了直截了当的方法
  • 如果先前的关注点有效,是否存在自动生成此类表达式的完全通用方法?

有人知道另一种方法吗?我可以想象可能有一种方法可以一步到位。但一小时后,我能想到的最好的就是这种二分法。

2 个答案:

答案 0 :(得分:16)

是。您可以使用:info命令。

Prelude> :info (+)
class Num a where
  (+) :: a -> a -> a
  ...
        -- Defined in `GHC.Num'
infixl 6 +

Prelude> :info (.)
(.) :: (b -> c) -> (a -> b) -> a -> c   -- Defined in `GHC.Base'
infixr 9 .

Prelude> :info ($)
($) :: (a -> b) -> a -> b       -- Defined in `GHC.Base'
infixr 0 $

如果它没有说,则默认为infixl 9

答案 1 :(得分:15)

@kqr的答案显然是来自GHCi的最佳答案,但我只是注意到类似于你尝试过的快速方式,我有时会在lambdabot的IRC频道中使用它(其中)不支持:info)。

>>> (0$0 !!)

由于部分要求(expr `op`)只能在expr `op` var(expr) `op` var完全相同地解析,以及$与最低固定性正确关联时才能使用!!无论!!的固定性如何​​,永远不会解析,并会在错误消息中为您提供{{1}}的固定性。