我从“残酷的Agda简介”http://oxij.org/note/BrutalDepTypes/
中接受了这一点假设我们想要在偶数上定义除以2。我们可以这样做:
div : (n : N) -> even n -> N
div zero p = zero
div (succ (succ n)) p= succ (div n p)
div (succ zero) ()
N是自然数,甚至是以下“命题”
even : N -> Set
even zero = \top
even (succ zero) = \bot
even (succ (succ n)) = even n
data \bot : Set where
record \top : Set where
评估表达式时 div(succ(succ zero)) 你得到 \ p - > succ零 这是你所期望的。但是,我不明白的是如何解释\ p。我也不明白代码是什么 f:\ bot - > ñ f = div(succ zero) 手段。据我所知,\ bot暗示A代表任何A ......所以类型是有效的。我想我认为依赖类型将允许我以这样的方式写div:div(succ zero)将更明确地失败类型检查。
有人可以就如何在agda中使用和查看谓词提出建议吗?
答案 0 :(得分:8)
您示例中的p
证明您要划分的数字(n
)确实是even
。如果您想使用div
功能划分数字,仅提供数字是不够的! Agda通过向您返回另一个函数来表示这一功能 - 这个函数使用2是even
的证明并返回1。
让我们写一些测试来看看它是如何工作的:
test₁ : ℕ
test₁ = div 4 ?
在这种情况下,洞的类型是毫无疑问的⊤
。 even 4
分两步缩小为⊤
,因此您可以轻松填写空白记录或仅提供下划线 - 只有一个可能的值,Agda可以填写:
test₁ = div 4 _
另一方面,当你给它一个奇数时,例如:
test₂ : ℕ
test₂ = div 3 ?
洞的类型减少到⊥
。 ⊥
是空类型;给出这种类型的价值相当于证明你的逻辑中存在矛盾。所以我们被困在这里 - 这正是我们想要的。
现在,你想让它感觉更多......隐含的,当我们试图给它一个奇数时,可能会引入编译错误。
Agda只能通过统一填写隐含的内容。虽然统一可以做一些复杂的事情,但有一些问题无法帮助;猜测证据就是其中之一。如果我们想隐瞒证据,我们必须保持简单。 even
就是一个很好的例子:我们可以从我们正在划分的数字中完整地计算它,它可以返回⊤
(可以简单填写)或⊥
(这意味着有什么不对的。)
div-implicit : (n : ℕ) {p : even n} → ℕ
div-implicit n {p} = div n p
偶数也很好:
test₃ : ℕ
test₃ = div-implicit 4
奇数会产生编译错误,请参阅我的previous answer。我们得到明亮的黄色,我不知道填写错误:
___10 : ⊥ [ at D:\Agda\SO6.agda:27,9-23 ]
这是一本关于如何通过Agda猜测琐碎证明义务的手册:
首先,我们需要一个确定参数是否正常的函数。通常,这是一个返回Bool
或Dec
。
check : ℕ → Bool
接下来,我们将Bool
转换为命题:
-- The Bool is true.
True : Bool → Set
True true = ⊤
True false = ⊥
-- The Bool is false.
False : Bool → Set
False true = ⊥
False false = ⊤
最后,我们可以包含一个隐含的参数,如果参数合适,Agda会填写,或者返回亮黄色:
f : (n : ℕ) {p : True (check n)} → ℕ
()
写成一个模式时,你断言任何人都无法提供该类型的参数 - 如果没有人能用这些参数实际调用函数,为什么还要写右手边?考虑一个例子:
f : (m n : ℕ) → m ≡ n → ℕ
f zero (suc zero) p = ?
f _ _ _ = ?
p
的类型是什么?这是0 ≡ 1
的证据 - 这显然是无意义的,没有人可以调用f 0 1
并提供0 ≡ 1
的证明。我们有理由将p
替换为()
并忽略右侧:
f zero (suc zero) ()
将此应用于div
示例:
div (suc zero) p = ?
现在,p
的类型为⊥
。任何人都无法提供⊥
类型的值。再说一遍,我们告知Agda这个案子是不可能的:
div (suc zero) ()
现在,那些问号是什么?关于agda-mode
的好处是它可以帮助您以合作的方式构建程序。当你在某个地方写?
时,你会告诉agda-mode
“我不知道这里发生了什么,让我们一起找出来。”
当您通过C-c C-l
检查文件时,Agda会将?
转换为 hole (这些是您看到的{ }0
。你可以在一个程序中有一个以上的洞,这解释了背后的数字。
现在,我说这些允许你以合作的方式建立计划,合作在哪里?你可以通过一些洞来做很多动作。例如,您可以询问其类型(通过C-c C-,
):
div : (n : ℕ) → even n → ℕ
div zero p = zero
div (suc (suc n)) p = { }0
div (suc zero) ()
-- Agda information buffer
Goal: ℕ
————————————————————————————————————————————————————————————
p : even n
n : ℕ
这三行告诉您,您最终必须写下ℕ
类型的目标(目标),并且在范围p
even n
和n
范围内类型为ℕ
。
你甚至可以在洞内写东西,询问你目前写的是什么类型(通过C-c C-.
):
div (suc (suc n)) p = {n }0
-- Agda information buffer
Goal: ℕ
Have: ℕ
————————————————————————————————————————————————————————————
p : even n
n : ℕ
当你满意的时候,你可以通过C-c C-space
用你在里面写的任何东西(好吧,只要它的类型)来替换这个洞。
如果你不关心实现(例如,你正在编写证据而且会做任何事情),你也可以告诉Agda尝试通过C-c C-a
来猜测它。当你有很多琐碎的案例时,这非常有用。
然后是案件分裂。编写函数时,通常需要进行模式匹配以了解有关参数的更多信息。 Agda允许您节省手动编写所有这些函数方程式的一些痛苦。
_+_ : (m n : ℕ) → ℕ
m + n = { }0
我们写一个变量,我们想要在洞内分割一个案例:
_+_ : (m n : ℕ) → ℕ
m + n = {m }0
然后按下神奇的C-c C-c
并瞧瞧:
_+_ : (m n : ℕ) → ℕ
zero + n = { }0
suc m + n = { }1