难以理解 Agda 的 Coinduction
Trouble to understand Agda's Coinduction
我正在尝试使用并行抢占式调度为 IMP 语言编写功能语义,如下文第 4 节所述paper。
我正在使用 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 的完整性检查器抱怨说 eval
中的几个点的终止检查失败] 和 close
函数。
我对这个问题的疑问是:
1) 为什么 Agda 终止检查会抱怨这些定义?在我看来,调用 δ (♯ close (eval s st))
很好,因为它已完成
在结构较小的语句上。
2) Current Agda's language documentation 表示这种基于乐谱的共归纳是 Agda 中的 "old-way" 共归纳。它建议使用
共归记录和共模。我环顾四周,但无法找到流和延迟 monad 之外的共模示例。我的问题:是否可以使用共归纳记录和共模来表示恢复?
让 Agda 相信这会终止的方法是使用大小类型。这样你就可以证明 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) = {!!}
我正在尝试使用并行抢占式调度为 IMP 语言编写功能语义,如下文第 4 节所述paper。
我正在使用 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 的完整性检查器抱怨说 eval
中的几个点的终止检查失败] 和 close
函数。
我对这个问题的疑问是:
1) 为什么 Agda 终止检查会抱怨这些定义?在我看来,调用 δ (♯ close (eval s st))
很好,因为它已完成
在结构较小的语句上。
2) Current Agda's language documentation 表示这种基于乐谱的共归纳是 Agda 中的 "old-way" 共归纳。它建议使用 共归记录和共模。我环顾四周,但无法找到流和延迟 monad 之外的共模示例。我的问题:是否可以使用共归纳记录和共模来表示恢复?
让 Agda 相信这会终止的方法是使用大小类型。这样你就可以证明 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) = {!!}