【问题标题】:Trouble to understand Agda's Coinduction难以理解 Agda 的 Coinduction
【发布时间】:2017-06-22 12:17:21
【问题描述】:

我正在尝试为具有并行抢占式调度的 IMP 语言编写函数语义,如以下paper 的第 4 节所述。

我正在使用 Agda 2.5.2 和标准库 0.13。此外,整个代码可在以下gist 获得。

首先,我已将相关语言的语法定义为归纳类型。

  data Exp (n : ℕ) : Set where
    $_  : ℕ → Exp n
    Var : Fin n → Exp n
    _⊕_ : Exp n → Exp n → Exp n

  data Stmt (n : ℕ) : Set where
    skip : Stmt n
    _≔_ : Fin n → Exp n → Stmt n
    _▷_ : Stmt n → Stmt n → Stmt n
    iif_then_else_ : Exp n → Stmt n → Stmt n → Stmt n
    while_do_ : Exp n → Stmt n → Stmt n
    _∥_ : Stmt n → Stmt n → Stmt n
    atomic : Stmt n → Stmt n
    await_do_ : Exp n → Stmt n → Stmt n

状态只是自然数的向量,表达式语义很简单。

  σ_ : ℕ → Set
  σ n = Vec ℕ n

  ⟦_,_⟧ : ∀ {n} → Exp n → σ n → ℕ
  ⟦ $ n , s ⟧ = n
  ⟦ Var v , s ⟧ = lookup v s
  ⟦ e ⊕ e' , s ⟧ = ⟦ e , s ⟧ + ⟦ e' , s ⟧

然后,我定义了恢复的类型,这是某种延迟计算。

  data Res (n : ℕ) : Set where
    ret : (st : σ n) → Res n
    δ   : (r : ∞ (Res n)) → Res n
    _∨_ : (l r : ∞ (Res n)) → Res n
    yield : (s : Stmt n)(st : σ n) → Res n

接下来,在1之后,我定义语句的顺序和并行执行

  evalSeq : ∀ {n} → Stmt n → Res n → Res n
  evalSeq s (ret st) = yield s st
  evalSeq s (δ r) = δ (♯ (evalSeq s (♭ r)))
  evalSeq s (l ∨ r) = ♯ evalSeq s (♭ l) ∨  ♯ evalSeq s (♭ r)
  evalSeq s (yield s' st) = yield (s ▷ s') st

  evalParL : ∀ {n} → Stmt n → Res n → Res n
  evalParL s (ret st) = yield s st
  evalParL s (δ r) = δ (♯ evalParL s (♭ r))
  evalParL s (l ∨ r) = ♯ evalParL s (♭ l) ∨ ♯ evalParL s (♭ r)
  evalParL s (yield s' st) = yield (s ∥ s') st

  evalParR : ∀ {n} → Stmt n → Res n → Res n
  evalParR s (ret st) = yield s st
  evalParR s (δ r) = δ (♯ evalParR s (♭ r))
  evalParR s (l ∨ r) = ♯ evalParR s (♭ l) ∨ ♯ evalParR s (♭ r)
  evalParR s (yield s' st) = yield (s' ∥ s) st

到目前为止,一切都很好。接下来,我需要定义语句评估函数与在恢复中关闭(执行暂停计算)的操作。

  mutual
    close : ∀ {n} → Res n → Res n
    close (ret st) = ret st
    close (δ r) = δ (♯ close (♭ r))
    close (l ∨ r) = ♯ close (♭ l) ∨ ♯ close (♭ r)
    close (yield s st) = δ (♯ eval s st)

    eval : ∀ {n} → Stmt n → σ n → Res n
    eval skip st = ret st
    eval (x ≔ e) st = δ (♯ (ret (st [ x ]≔ ⟦ e , st ⟧ )))
    eval (s ▷ s') st = evalSeq s (eval s' st)
    eval (iif e then s else s') st with ⟦ e , st ⟧
    ...| zero = δ (♯ yield s' st)
    ...| suc n = δ (♯ yield s st)
    eval (while e do s) st with ⟦ e , st ⟧
    ...| zero = δ (♯ ret st)
    ...| suc n = δ (♯ yield (s ▷ while e do s) st )
    eval (s ∥ s') st = (♯ evalParR s' (eval s st)) ∨ (♯ evalParL s (eval s' st))
    eval (atomic s) st = {!!} -- δ (♯ close (eval s st))
    eval (await e do s) st = {!!}

当我尝试用δ (♯ close (eval s st)) 填充eval 构造函数的eval 方程中的漏洞时,Agda 的整体检查器抱怨说终止检查在evalclose 函数中的多个点都失败了。

我对这个问题的问题是:

1) 为什么 Agda 终止检查会抱怨这些定义?在我看来,电话δ (♯ close (eval s st)) 很好,因为它已经完成了 在一个结构更小的陈述上。

2) Current Agda's language documentation 说这种基于乐谱的联合归纳法是 Agda 中的“旧方式”联合归纳法。它建议使用 共归纳记录和共同模式。我环顾四周,但我无法找到流和延迟单子之外的共同模式示例。我的问题:是否可以使用共归纳记录和共同模式来表示恢复?

【问题讨论】:

  • wrt 1:δ (♯ close (eval s st)) 中的 eval 使用结构上较小的参数调用,close 也会在结果上调用 eval,参数未知大小。所以不能用归纳法证明 eval 的终止。

标签: agda coinduction


【解决方案1】:

让 Agda 相信它会终止的方法是使用 sized 类型。这样你就可以证明close x 的定义至少与x 一样好。

首先,这里是Res的定义,基于共归纳记录和大小类型:

mutual
  record Res (n : ℕ) {sz : Size} : Set where
    coinductive
    field resume : ∀ {sz' : Size< sz} → ResCase n {sz'}
  data ResCase (n : ℕ) {sz : Size} : Set where
    ret : (st : σ n) → ResCase n
    δ   : (r : Res n {sz}) → ResCase n
    _∨_ : (l r : Res n {sz}) → ResCase n
    yield : (s : Stmt n) (st : σ n) → ResCase n
open Res

那么你可以证明evalSeq和朋友们保持大小:

evalStmt : ∀ {n sz} → (Stmt n → Stmt n → Stmt n) → Stmt n → Res n {sz} → Res n {sz}
resume (evalStmt _⊗_ s res) with resume res
resume (evalStmt _⊗_ s _) | ret st = yield s st
resume (evalStmt _⊗_ s _) | δ x = δ (evalStmt _⊗_ s x)
resume (evalStmt _⊗_ s _) | l ∨ r = evalStmt _⊗_ s l ∨ evalStmt _⊗_ s r
resume (evalStmt _⊗_ s _) | yield s' st = yield (s ⊗ s') st

evalSeq : ∀ {n sz} → Stmt n → Res n {sz} → Res n {sz}
evalSeq = evalStmt (\s s' → s ▷ s')

evalParL : ∀ {n sz} → Stmt n → Res n {sz} → Res n {sz}
evalParL = evalStmt (\s s' → s ∥ s')

evalParR : ∀ {n sz} → Stmt n → Res n {sz} → Res n {sz}
evalParR = evalStmt (\s s' → s' ∥ s)

close 也是如此:

mutual
  close : ∀ {n sz} → Res n {sz} → Res n {sz}
  resume (close res) with resume res
  ... | ret st = ret st
  ... | δ r = δ (close r)
  ... | l ∨ r = close l ∨ close r
  ... | yield s st = δ (eval s st)

并且eval 定义良好,适用于任何大小:

  eval : ∀ {n sz} → Stmt n → σ n → Res n {sz}
  resume (eval skip st) = ret st
  resume (eval (x ≔ e) st) = ret (st [ x ]≔ ⟦ e , st ⟧ )
  resume (eval (s ▷ s') st) = resume (evalSeq s (eval s' st))
  resume (eval (iif e then s else s') st) with ⟦ e , st ⟧
  ...| zero = yield s' st
  ...| suc n = yield s st
  resume (eval (while e do s) st) with ⟦ e , st ⟧
  ...| zero = ret st
  ...| suc n = yield (s ▷ while e do s) st
  resume (eval (s ∥ s') st) = evalParR s' (eval s st) ∨ evalParL s (eval s' st)
  resume (eval (atomic s) st) = resume (close (eval s st)) -- or δ
  resume (eval (await e do s) st) = {!!}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-28
    • 2014-11-15
    • 2014-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多