使用Church编码,可以在不使用内置ADT系统的情况下表示任意代数数据类型。例如,Nat
可以表示为(例如在Idris中):
-- Original type
data Nat : Type where
natSucc : Nat -> Nat
natZero : Nat
-- Lambda encoded representation
Nat : Type
Nat = (Nat : Type) -> (Nat -> Nat) -> Nat -> Nat
natSucc : Nat -> Nat
natSucc pred n succ zero = succ (pred n succ zero)
natZero : Nat
natZero n succ zero = zero
Pair
可以表示为:
-- Original type
data Pair_ : (a : Type) -> (b : Type) -> Type where
mkPair_ : (x:a) -> (y:b) -> Pair_ a b
-- Lambda encoded representation
Par : Type -> Type -> Type
Par a b = (t:Type) -> (a -> b -> t) -> t
pair : (ta : Type) -> (tb : Type) -> (a:ta) -> (b:tb) -> Par ta tb
pair ta tb a b k t = t a b
fst : (ta:Type) -> (tb:Type) -> Par ta tb -> ta
fst ta tb pair = pair ta (\ a, b => a)
snd : (ta:Type) -> (tb:Type) -> Par ta tb -> tb
snd ta tb pair = pair tb (\ a, b => b)
等等。现在,编写这些类型,构造函数,匹配器是一项非常机械的任务。我的问题是:是否可以将ADT表示为类型级别的规范,然后自动派生类型(即Nat
/ Par
)以及构造函数/析构函数那些规格?同样,我们可以使用这些规范来推导泛型吗?例如:
NAT : ADT
NAT = ... some type level expression ...
Nat : Type
Nat = DeriveType NAT
natSucc : ConstructorType 0 NAT
natSucc = Constructor 0 NAT
natZero : ConstructorType 1 NAT
natZero = Constructor 1 NAT
natEq : EqType NAT
natEq = Eq NAT
natShow : ShowType NAT
natShow = Show NAT
... and so on
答案 0 :(得分:6)
索引描述并不比多项式仿函数更难。考虑一下propositional descriptions的简单形式:
data Desc (I : Set) : Set₁ where
ret : I -> Desc I
π : (A : Set) -> (A -> Desc I) -> Desc I
_⊕_ : Desc I -> Desc I -> Desc I
ind : I -> Desc I -> Desc I
π
与Emb
类似,后跟|*|
,但它允许其余描述取决于类型A
的值。 _⊕_
与|+|
相同。 ind
类似于Rec
,后跟|*|
,但它也会收到未来子项的索引。 ret
完成描述并指定构造术语的索引。这是一个直接的例子:
vec : Set -> Desc ℕ
vec A = ret 0
⊕ π ℕ λ n -> π A λ _ -> ind n $ ret (suc n)
vec
的第一个构造函数不包含任何数据并构造长度为0
的向量,因此我们放置ret 0
。第二个构造函数接收子向量的长度(n
),类型A
的某个元素和子向量,它构造一个长度为suc n
的向量。
构造固定的描述点也类似于多项式算子的
⟦_⟧ : ∀ {I} -> Desc I -> (I -> Set) -> I -> Set
⟦ ret i ⟧ B j = i ≡ j
⟦ π A D ⟧ B j = ∃ λ x -> ⟦ D x ⟧ B j
⟦ D ⊕ E ⟧ B j = ⟦ D ⟧ B j ⊎ ⟦ E ⟧ B j
⟦ ind i D ⟧ B j = B i × ⟦ D ⟧ B j
data μ {I} (D : Desc I) j : Set where
node : ⟦ D ⟧ (μ D) j -> μ D j
Vec
只是
Vec : Set -> ℕ -> Set
Vec A = μ (vec A)
之前它是adt Rec t = t
,但现在条款被编入索引,因此t
也被编入索引(上面称为B
)。 ind i D
带有应用i
的子项μ D
的索引。因此,在解释向量的第二个构造函数时,Vec A
将应用于子向量n
的长度(来自ind n $ ...
),因此子项的类型为Vec A n
。
在最终的ret i
案例中,要求构造的术语具有与预期相同的索引(i
)(j
)。
为这些数据类型派生消除器稍微复杂一些:
Elim : ∀ {I B} -> (∀ {i} -> B i -> Set) -> (D : Desc I) -> (∀ {j} -> ⟦ D ⟧ B j -> B j) -> Set
Elim C (ret i) k = C (k refl)
Elim C (π A D) k = ∀ x -> Elim C (D x) (k ∘ _,_ x)
Elim C (D ⊕ E) k = Elim C D (k ∘ inj₁) × Elim C E (k ∘ inj₂)
Elim C (ind i D) k = ∀ {y} -> C y -> Elim C D (k ∘ _,_ y)
module _ {I} {D₀ : Desc I} (P : ∀ {j} -> μ D₀ j -> Set) (f₀ : Elim P D₀ node) where
mutual
elimSem : ∀ {j}
-> (D : Desc I) {k : ∀ {j} -> ⟦ D ⟧ (μ D₀) j -> μ D₀ j}
-> Elim P D k
-> (e : ⟦ D ⟧ (μ D₀) j)
-> P (k e)
elimSem (ret i) z refl = z
elimSem (π A D) f (x , e) = elimSem (D x) (f x) e
elimSem (D ⊕ E) (f , g) (inj₁ x) = elimSem D f x
elimSem (D ⊕ E) (f , g) (inj₂ y) = elimSem E g y
elimSem (ind i D) f (d , e) = elimSem D (f (elim d)) e
elim : ∀ {j} -> (d : μ D₀ j) -> P d
elim (node e) = elimSem D₀ f₀ e
我详细阐述了elsewhere。
可以像这样使用:
elimVec : ∀ {n A}
-> (P : ∀ {n} -> Vec A n -> Set)
-> (∀ {n} x {xs : Vec A n} -> P xs -> P (x ∷ xs))
-> P []
-> (xs : Vec A n)
-> P xs
elimVec P f z = elim P (z , λ _ -> f)
获得可判定的平等更加冗长,但并不困难:它只是要求Set
收到的每个π
具有可判定的平等性。如果数据类型的所有非递归内容都具有可判定的相等性,那么您的数据类型也具有可判定的相等性。
答案 1 :(得分:3)
为了帮助您入门,这里有一些代表多项式仿函数的Idris代码:
infix 10 |+|
infix 10 |*|
data Functor : Type where
Rec : Functor
Emb : Type -> Functor
(|+|) : Functor -> Functor -> Functor
(|*|) : Functor -> Functor -> Functor
LIST : Type -> Functor
LIST a = Emb Unit |+| (Emb a |*| Rec)
TUPLE2 : Type -> Type -> Functor
TUPLE2 a b = Emb a |*| Emb b
NAT : Functor
NAT = Rec |+| Emb Unit
以下是对其固定点的基于数据的解释(有关详细信息,请参阅http://www.cse.chalmers.se/~ulfn/papers/afp08/tutorial.pdf中的3.2)
adt : Functor -> Type -> Type
adt Rec t = t
adt (Emb a) _ = a
adt (f |+| g) t = Either (adt f t) (adt g t)
adt (f |*| g) t = (adt f t, adt g t)
data Mu : (F : Functor) -> Type where
Fix : {F : Functor} -> adt F (Mu F) -> Mu F
这是基于教会代表的解释:
Church : Functor -> Type
Church f = (t : Type) -> go f t t
where
go : Functor -> Type -> (Type -> Type)
go Rec t = \r => t -> r
go (Emb a) t = \r => a -> r
go (f |+| g) t = \r => go f t r -> go g t r -> r
go (f |*| g) t = go f t . go g t
所以我们可以这样做。
-- Need the prime ticks because otherwise clashes with Nat, zero, succ from the Prelude...
Nat' : Type
Nat' = Mu NAT
zero' : Nat'
zero' = Fix (Right ())
succ' : Nat' -> Nat'
succ' n = Fix (Left n)
但也
zeroC : Church NAT
zeroC n succ zero = (zero ())
succC : Church NAT -> Church NAT
succC pred n succ zero = succ (pred n succ zero)