Liquid Haskell:内联递归函数中的“循环类型别名定义”错误

时间:2018-11-21 19:06:47

标签: haskell refinement-type liquid-haskell

我在Haskell中编写了一些代码来做ordinal arithmetic,现在正尝试使用Liquid Haskell验证某些属性。但是,我在“反映”递归函数时遇到了麻烦。我在下面的“小于”函数中隔离了一个问题:

-- (Ord a n b) = a^n + b
{-@ data Ordinal [size] = Ord { a :: Ordinal, n :: Nat,  b :: Ordinal }
                        | Zero {} @-}
data Ordinal = Ord Ordinal Integer Ordinal
             | Zero
             deriving (Eq, Show)

{-@ measure size @-}
{-@ size :: Ordinal -> Nat @-}
size :: Ordinal -> Integer
size Zero = 1
size (Ord a n b) = (size a) + 1 + (size b)

{-@ inline ordLT @-}
ordLT :: Ordinal -> Ordinal -> Bool
ordLT _ Zero = False
ordLT Zero _ = True
ordLT (Ord a0 n0 b0) (Ord a1 n1 b1) =
    (ordLT a0 a1) || 
    (a0 == a1 && n0 < n1) || 
    (a0 == a1 && n0 == n1 && ordLT b0 b1)

one = (Ord Zero 1 Zero)     -- 1
w   = (Ord one 1 Zero)      -- omega
main = print w              -- Ord (Ord Zero 1 Zero) 1 Zero

仅通过上述操作执行liquid ordinals.hs会出现以下错误:

Error: Cyclic type alias definition for `Main.ordLT`
14 |     {-@ inline ordLT @-}
                     ^^^^^
The following alias definitions form a cycle:
* `Main.ordLT`

那么反映递归函数的正确方法是什么?我已经读过liquid haskell tutorial,但无法弄清楚其示例在做什么方面的差异。

1 个答案:

答案 0 :(得分:2)

要在没有更多上下文的情况下确定要执行的操作有些困难,但是inline确实不适用于递归函数:inline允许您使用类型为的函数通过在编译时进行扩展(在创建发送到求解器的验证条件之前),因此有必要用某种特定的逻辑公式替换ordLT a b的每次出现(这是不可能的,因为它是递归的。)

如果您需要能够在逻辑中使用任意Haskell函数,则可以考虑使用优化反射。您的示例使用{-@ reflect ordLT @-}{-@ LIQUID "--exact-data-cons" @-}进行编译。但是,在逻辑中不会自动完全解释通过优化反射创建的功能符号。 this paper中讨论了具体细节,these slidesthis blog post中提供了更易于理解的示例/解释。要记住的主要要点是,通过反射创建的ordLT符号在逻辑上最初将被视为一个完全未解释的函数:LH唯一了解的就是a0 == a1 ==> b0 == b1 ==> ordLT a0 b0 == ordLT a1 b1之类的东西(如果您调用它在相同的输入上,结果是相同的。)

为了在逻辑上对ordLT做一些有用的事情,您将需要在范围内某处的值级别调用ordLT,这将揭示该特定调用的值,因为ordLT的返回类型(值级别的函数)在这些输入上声明有关ordLT(逻辑级别的未解释函数)的输出的某些信息。上面和本文链接的幻灯片中提供了示例。我希望这足以让您入门!