【问题标题】:Is it possible to create a type-level representation of generic ADTs?是否可以创建通用 ADT 的类型级表示?
【发布时间】:2016-10-30 19:38:07
【问题描述】:

使用 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

【问题讨论】:

  • 请注意,据我所知,这些 Church-encodings 缺少依赖消除。例如。如果 Bool = (A : Type) -> A -> A -> Atru,fls 被相应定义,则无法证明 (b: Bool) -> (b = tru \/ b = fls),但可以使用归纳类型。
  • 您可以为更具表现力的类型系统做到这一点:完全依赖类型 + 归纳系列。派生Nat 然后是消除器Nat = (P : Nat -> Type) -> (forall n. P n -> P (succ n)) -> P 0 -> forall n. P n。我为此写了blog postEqType 也是 derivableexample)。你想覆盖多少类型?只有 System F 类型,没有 GADT?
  • @chi:确实,这似乎是证明您的说法的来源:Induction Is Not Derivable in Second Order Dependent Type Theory
  • @Cactus,您可以使用内部参数化恢复 Church 编码数据类型的消除器。 No references,不过。

标签: functional-programming agda algebraic-data-types idris church-encoding


【解决方案1】:

索引描述并不比多项式仿函数难。考虑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 情况下,要求构造的术语具有与预期 (j) 相同的索引 (i)。

为此类数据类型派生消除器稍微复杂一些:

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 具有可判定相等性。如果您的数据类型的所有非递归内容都具有可判定的相等性,那么您的数据类型也具有它。

The code.

【讨论】:

    【解决方案2】:

    为了帮助您入门,这里有一些表示多项式仿函数的 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)
    

    【讨论】:

    • 这可以扩展到多项式函子的固定点之外吗?例如。使用descriptions?
    • 比我想象的要深刻且简单得多。其次,我希望将其扩展为用简单的术语解释描述。
    • @Benjamin Hodgson,我添加了与描述相关的答案。
    猜你喜欢
    • 1970-01-01
    • 2016-09-25
    • 2017-11-15
    • 1970-01-01
    • 1970-01-01
    • 2014-06-11
    • 2023-04-10
    • 1970-01-01
    • 2020-04-20
    相关资源
    最近更新 更多