我对依赖类型语言感兴趣。有限数字对我来说似乎非常有用。例如,安全地索引固定大小的数组。但这个定义对我来说并不清楚。
Idris中有限数据的数据类型如下:(并且在Agda中可能类似)
data FiniteNum : Natural -> Type where
FZero : FiniteNum (Succ k)
FSucc : FiniteNum k -> FiniteNum (Succ k)
似乎有效:
exampleFN : FiniteNum (Succ (Succ Zero))
exampleFN = FSucc FZero -- typechecks
-- exampleFN = FSucc (FSucc FZero) -- won't typecheck
但这是如何运作的? k是什么意思?为什么类型检查器接受第一个实现并拒绝第二个?
答案 0 :(得分:9)
将索引视为可以由Fin n
表示的任何数字的上限。例如,Fin 4
包含小于4
的所有自然数。这是一个数据声明:
data Fin : ℕ → Set where
该定义与此有何关系?自然数的定义有两部分:zero
和suc
;对于Fin
,我们会调用fzero
和fsuc
。
使用我们的上限解释,fzero
可以给出任何上限,只要它不是zero
(0≮0)。我们如何表示绑定可以是zero
以外的任何其他内容?我们可以通过应用suc
:
fzero : {k : ℕ} → Fin (suc k)
这正是我们所需要的:没有人能够以fzero
类型的方式编写Fin 0
。
现在fsuc
案例:我们有一个上限为k
的数字,我们想要创建一个后继者。关于上限我们能说些什么?肯定会增加至少一个:
fsuc : {k : ℕ} → Fin k → Fin (suc k)
作为练习,请说服自己所有小于n
的数字都可以用Fin n
表示(只有一种可能的方式)。
类型检查器如何接受并拒绝另一个?在这种情况下,它是简单的统一。我们来看看这段代码:
test : Fin (suc (suc zero))
test = ?
当我们写fsuc
时,Agda必须弄清k
的值。要做到这一点,需要查看fsuc
构造函数:它构造一个Fin (suc k)
类型的值,我们需要suc k = suc (suc zero)
;通过统一这两个,我们得到k = suc zero
。所以接下来:
test = fsuc ?
现在,fsuc
后面的表达式(由?
表示, hole )的表达式为Fin (suc zero)
(自k = suc zero
起)。当我们填写fzero
时,Agda会尝试将suc zero
与suc k₂
统一起来,这当然会成功解决k₂ = zero
。
如果我们决定投入另一个fsuc
:
test = fsuc (fsuc ?)
然后使用与上面相同的统一,我们得到洞的类型必须是Fin zero
。到目前为止,这种类型检查很好。但是,当您尝试将fzero
填入其中时,Agda必须将zero
与suc k₃
统一起来 - 无论k₃
,suc
的价值如何永远不会是zero
,因此失败并且您会收到类型错误。
有限数的另一种表示(可以说是不太适合使用)是一对自然数,并证明它小于界限。
open import Data.Product
Fin' : ℕ → Set
Fin' n = Σ ℕ (λ k → k < n)
原始Fin
可以被视为Fin'
的版本,其中证明直接烘焙到构造函数中。
答案 1 :(得分:1)
简短的回答是FiniteNum Zero类型为空,因为两个构造函数都返回以非零自然数索引的FiniteNum。现在尝试回答以下问题:FiniteNum(Succ Zero)有多少个元素?他们看起来怎么样?重复2,3,4 ...