如何检查与特定名称关联的agda术语是否依赖于漏洞?

时间:2017-11-09 03:35:53

标签: emacs agda agda-mode

出于脚本的目的,我想查询agda编译器关于agda源文件中函数的定义。我想问一个问题: X 命名的函数是否依赖于某个洞? (即它是“完整的证据”,还是“正在进行的证明”)。其中 X 是源文件中函数的名称。

例如,请使用以下示例源文件:

open import Relation.Binary.PropositionalEquality

postulate
  A : Set
  x : A
  y : A
  z : A  
  q1 : x ≡ y
  q2 : y ≡ z

pf1 : x ≡ z
pf1 = trans q1 q2 

pf2 : z ≡ x
pf2 rewrite q1 | q2 = refl

我希望能够(在我的脚本中)确定pf2是否依赖任何漏洞?在这种情况下,答案是否定的。

或者,假设该文件类似于:

open import Relation.Binary.PropositionalEquality

postulate
  A : Set
  x : A
  y : A
  z : A  
  q1 : x ≡ y
  q2 : y ≡ z

pf1 : x ≡ z
pf1 = trans q1 q2 

lemma1 : z ≡ y
lemma1 = {!!}

pf2 : z ≡ x
pf2 rewrite q1 = lemma1

现在上面提出的问题的回答是“是”:pf2是不完整的,因为它依赖于一个洞(间接地,通过引理1)。

我知道我可以找到问题的答案:这个agda源文件中有任何函数是否依赖于漏洞。当我们在源文件上运行agda编译器时,如果存在“未解决的交互元素”,则返回状态将为1,如果一切都已完成,则状态将为0。但是,我想知道源文件中特定函数(按名称)是否具有“未解决的交互元数据”的详细信息。

有没有办法做到这一点?

我查看了agda交互模式的源代码(agda-mode emacs代码使用的接口),但似乎为交互模式定义的大多数命令都依赖于字符范围而不是符号,所以我还没有找到从交互模式获取此信息的方法。

编辑:基于user3237465的评论,我研究了使用反射来解决这个问题。看起来它可以工作,但重写有问题。例如,假设我们在emacs中加载了以下文件:

open import Relation.Binary.PropositionalEquality
open import Agda.Builtin.Reflection

postulate
  A : Set
  x : A
  y : A
  z : A  
  q1 : x ≡ y
  q2 : y ≡ z

pf1 : x ≡ z
pf1 = trans q1 q2 

lemma1 : z ≡ y
lemma1 = {!!}

pf2 : z ≡ x
pf2 rewrite q1 = lemma1

pf3 : z ≡ x
pf3 = trans lemma1 (sym q1)

-- user3237465 suggested this macro.
-- unfortunately, normalizing `test`
-- using this macro still doesn't show
-- information about the contents of
-- lemma1
macro
  actualQuote : Term -> Term -> TC _
  actualQuote term hole =
    bindTC (normalise term) λ nterm ->
    bindTC (quoteTC nterm) (unify hole)

test = actualQuote pf2
test2 = actualQuote pf3
test3 = actualQuote pf1

如果我输入C-c C-n并输入quoteTC pf3,则会输出quoteTC (trans ?0 (sym q1))。这就是我想要的,因为它表明证据取决于一个洞。

另一方面,如果我输入C-c C-n并输入quoteTC pf2,则输出quoteTC (pf2 | x | q1)。因此,规范化过程似乎无法通过重写。

有没有人知道是否有解决方法?

EDIT2:使用user3237465宏的pf2标准化是:

def (quote .test4.rewrite-20)
(arg (arg-info visible relevant)
 (def (quote x) .Agda.Builtin.List.List.[])
 .Agda.Builtin.List.List.∷
 arg (arg-info visible relevant)
 (def (quote q1) .Agda.Builtin.List.List.[])
 .Agda.Builtin.List.List.∷ .Agda.Builtin.List.List.[])

1 个答案:

答案 0 :(得分:2)

这个答案是关于使用反射来解决问题。

您尝试中缺少的是使用getDefinition查看已定义的函数。

以下是使用agda-prelude(https://github.com/UlfNorell/agda-prelude)的完整示例,因为我没有时间用标准库(读者练习)来做这件事。

open import Prelude
open import Tactic.Reflection
open import Control.Monad.State
open import Container.Traversable

我们需要跟踪我们已经查看过哪些名称以避免在递归函数上循环,所以让我们使用状态monad。

M = StateT (List Name) TC

runM : {A : Set} → M A → TC A
runM m = fst <$> runStateT m []

isVisited : Name → M Bool
isVisited x = gets (elem x)

setVisited : Name → M ⊤
setVisited x = _ <$ modify (x ∷_)

anyM : {A : Set} → (A → M Bool) → List A → M Bool
anyM p xs = foldr _||_ false <$> traverse p xs

不幸的是,我们无法说服终止检查器只能存在有限数量的已定义函数,所以让我们作弊。如果我们没有深度,那么无作弊选项将设置深度限制并返回true(或不知道)。

{-# TERMINATING #-}
anyMetas : Term → M Bool

checkClause : Clause → M Bool
checkClause (clause ps t) = anyMetas t
checkClause (absurd-clause ps) = return false

checkName : Name → M Bool
checkName f = do
  false ← isVisited f
    where true → return false
  function cs ← lift (getDefinition f)
    where _ → return false
  anyM checkClause cs

我忍不住对checkName使用do-notation,因为它使代码变得更好。如果您没有从github构建最新的Agda,可以使用注释代码:

  -- caseM isVisited f of λ where
  --   true → return false
  --   false → setVisited f >>
  --     (caseM lift (getDefinition f) of λ where
  --       (function cs) → anyM checkClause cs
  --       _ → return false)

anyMetaArgs = anyM (anyMetas ∘ unArg)

checkSort : Sort → M Bool
checkSort (set t) = anyMetas t
checkSort (lit n) = return false
checkSort unknown = return false

anyMetas (var x args) = anyMetaArgs args
anyMetas (con c args) = anyMetaArgs args
anyMetas (def f args) = (| checkName f || anyMetaArgs args |)
anyMetas (lam v t) = anyMetas (unAbs t)
anyMetas (pat-lam cs args) = (| anyM checkClause cs || anyMetaArgs args |)
anyMetas (pi a b) = (| anyMetas (unArg a) || anyMetas (unAbs b) |)
anyMetas (agda-sort s) = checkSort s
anyMetas (lit l) = return false
anyMetas (meta x x₁) = return true
anyMetas unknown = return false

使用anyMetas函数,我们可以定义一个带有名称的宏,并返回一个布尔值,指示名称是否取决于元。

macro
  dependsOnMeta? : Name → Term → TC ⊤
  dependsOnMeta? x hole = unify hole =<< quoteTC =<< runM (anyMetas (def x []))

您的测试用例现在通过

postulate
  A : Set
  x : A
  y : A
  z : A
  q1 : x ≡ y
  q2 : y ≡ z

pf1 : x ≡ z
pf1 = trans q1 q2

lemma1 : z ≡ y
lemma1 = {!!}

pf2 : z ≡ x
pf2 rewrite q1 = lemma1

pf3 : z ≡ x
pf3 = trans lemma1 (sym q1)

test1 : dependsOnMeta? pf1 ≡ false
test1 = refl

test2 : dependsOnMeta? pf2 ≡ true
test2 = refl

test3 : dependsOnMeta? pf3 ≡ true
test3 = refl