类型级固定点,同时确保终止

Type Level Fix Point while Ensuring Termination

可以向某些人表示如何使 unFix 总计?可能通过限制 f 是什么?

record Fix (f : Type -> Type) where
    constructor MkFix
    unFix : f (Fix f)

> :total unFix
Fix.unFix is possibly not total due to:
    MkFix, which is not strictly positive

这里的问题是 Idris 无法知道您为数据类型使用的基本仿函数是否严格为正。如果它接受你的定义,你就可以将它与具体的负函子一起使用,并从中证明 Void

有两种表示严格正函子的方法:定义一个宇宙或使用容器。我已经把所有的东西都放在 two self-contained gists 里了(但是那里没有评论)。

严格正函子的宇宙

您可以从基本表示开始:我们可以将函子分解为 sigma 类型 (Sig)、递归子结构的(严格正向)位置 (Rec) 或什么都没有(End)。这给了我们这个描述及其作为 Type -> Type 函数的解码:

-- A universe of positive functor
data Desc : Type where
  Sig : (a : Type) -> (a -> Desc) -> Desc
  Rec : Desc -> Desc
  End : Desc


-- The decoding function
desc : Desc -> Type -> Type
desc (Sig a d) x = (v : a ** desc (d v) x)
desc (Rec d)   x = (x, desc d x)
desc End       x = ()

一旦你有了这个保证严格为正的函子域,你就可以取它们的最小不动点:

-- The least fixpoint of such a positive functor
data Mu : Desc -> Type where
  In : desc d (Mu d) -> Mu d

您现在可以定义自己喜欢的数据类型。

示例:自然

我们从每个构造函数的总和类型标签开始。

data NatC = ZERO | SUCC

然后,我们通过将构造函数标记存储在 sigma 中并根据标记值计算其余描述来定义严格正基函子。 ZERO 标签与 End 关联(zero 构造函数中没有其他内容可存储),而 SUCC 标签需要 Rec End,即说一个对应于 Nat 的前身的递归子结构。

natD : Desc
natD = Sig NatC $ \ c => case c of
  ZERO => End
  SUCC => Rec End

然后我们的归纳类型通过取描述的不动点得到:

nat : Type
nat = Mu natD

我们自然可以恢复我们期望的构造函数:

zero : nat
zero = In (ZERO ** ())

succ : nat -> nat
succ n = In (SUCC ** (n, ()))

参考资料

这个特定的宇宙取自 McBride 的 'Ornamental Algebras, Algebraic Ornaments',但您可以在 Chapman、Dagand、McBride 和 Morris 的 'The Gentle Art of Levitation' 中找到更多详细信息。

作为容器扩展的严格正函子

第二种表示是基于另一种分解:每个归纳类型被看作是一个通用的形状(即它的构造函数和它们存储的数据)加上一些递归位置(这可以取决于形状的具体值).

record Container where
  constructor MkContainer
  shape : Type
  position : shape -> Type

我们可以再次将其解释为 Type -> Type 函数:

container : Container -> Type -> Type
container (MkContainer s p) x = (v : s ** p v -> x)

并取如此定义的严格正函子的不动点:

data W : Container -> Type where
  In : container c (W c) -> W c

您可以通过定义 Container 感兴趣的数据类型再次恢复您最喜欢的数据类型。

示例:自然

自然数有两个构造函数,每个构造函数都不存储任何内容。所以形状将是 Bool。如果我们在 zero 的情况下,则没有递归位置 (Void),而在 succ 的情况下,只有一个 (()).

natC : Container
natC = MkContainer Bool (\ b => if b then Void else ())

我们的类型是通过容器的固定点得到的:

nat : Type
nat = W natC

并且我们可以恢复通常的构造函数:

zero : nat
zero = In (True ** \ v => absurd v)

succ : nat -> nat
succ n = In (False ** \ _ => n)

参考资料

这是基于 Abbott、Altenkirch 和 Ghani 的 'Containers: Constructing Strictly Positive Types'。