简体   繁体   English

玫瑰树的初始代数

[英]Initial algebra for rose trees

As far as I understand, recursive data types from Haskell correspond to initial algebras of endofunctors from the Hask category [ 1 , 2 ]. 据我明白,从Haskell的递归数据类型对应于endofunctors的初始代数从Hask类别[ 12 ]。 For example: 例如:

  • Natural numbers, data Nat = Zero | Succ Nat 自然数, data Nat = Zero | Succ Nat data Nat = Zero | Succ Nat , correspond to the initial algebra of the endofunctor F(-) = 1 + (-) . data Nat = Zero | Succ Nat ,对应于endofunctor F(-) = 1 + (-)的初始代数。
  • Lists, data List a = Nil | Cons a (List a) 列表, data List a = Nil | Cons a (List a) data List a = Nil | Cons a (List a) , correspond to the initial algebra of the endofunctor F(A, -) = 1 + A × (-) . data List a = Nil | Cons a (List a) ,对应于endofunctor F(A, -) = 1 + A × (-)的初始代数。

However, it's not clear to me what the endofunctor corresponding to the rose trees should be: 但是,我不清楚对应玫瑰树的endofunctor应该是什么:

data Rose a = Node a (List (Rose a))

What confuses me is that there are two recursions: one for the rose tree and the other for the list. 令我困惑的是,有两个递归:一个用于玫瑰树,另一个用于列表。 According to my calculations, I would get the following functor, but it doesn't seem right: 根据我的计算,我会得到以下仿函数,但它似乎不正确:

F(A, •, -) = A × (1 + (-) × (•))

Alternatively, rose trees can be defined as mutually recursive data types: 或者,可以将玫瑰树定义为相互递归的数据类型:

data Rose a   = Node a (Forest a)
type Forest a = List (Rose a)

Do mutually recursive data types have an interpretation in category theory? 相互递归数据类型在类别理论中有解释吗?

I would discourage talk of "the Hask Category" because it subconsciously conditions you against looking for other categorical structure in Haskell programming. 我会劝阻谈论“Hask类别”,因为它潜意识地阻止你在Haskell编程中寻找其他分类结构。

Indeed, rose trees can be seen as the fixpoint of an endofunctor on types-and-functions, a category which we might be better to call Type , now that Type is the type of types. 实际上,玫瑰树可以看作是类型和函数的endofunctor的固定点,我们可能更好地调用Type ,现在Type是类型的类型。 If we give ourselves some of the usual functor kit... 如果我们给自己一些通常的仿函数套件......

newtype K a   x = K a deriving Functor           -- constant functor
newtype P f g x = P (f x, g x) deriving Functor  -- products

...and fixpoints... ......和固定点......

newtype FixF f = InF (f (FixF f))

...then we may take ......那我们可以接受

type Rose a = FixF (P (K a) [])
pattern Node :: a -> [Rose a] -> Rose a
pattern Node a ars = InF (P (K a, ars))

The fact that [] is itself recursive does not prevent its use in the formation of recursive datatypes via Fix . []本身是递归的这一事实并不妨碍它通过Fix在形成递归数据类型时使用。 To spell out the recursion explicitly, we have nested fixpoints, here with bound variable names chosen suggestively: 为了明确地拼出递归,我们有嵌套的固定点,这里有一些暗示选择的绑定变量名:

Rose a = μrose. a * (μlist. 1 + (rose * list))

Now, by the time we've arrived in the second fixpoint, we have a type formula 现在,当我们到达第二个固定点时,我们有一个类型公式

1 + (rose * list)

which is functorial (indeed, strictly positive) in both rose and list . roselist中都是functorial(确实是严格正面的)。 One might say it is a Bifunctor , but that's unnecessary terminology: it's a functor from (Type, Type) to Type . 有人可能会说它是Bifunctor ,但那是不必要的术语:它是从(Type, Type)TypeBifunctor函数。 You can make a Type -> Type functor by taking a fixpoint in the second component of the pair, and that's just what happened above. 您可以通过在对的第二个组件中使用一个固定点来创建一个Type -> Type仿函数,这就是上面发生的事情。

The above definition of Rose loses an important property. Rose的上述定义失去了重要的属性。 It is not true that 这不是真的

Rose :: Type -> Type   -- GHC might say this, but it's lying

merely that Rose x :: Type if x :: Type . 仅仅是Rose x :: Type if x :: Type In particular, 特别是,

Functor Rose

is not a well typed constraint, which is a pity, as intuitively, rose trees ought to be functorial in the elements they store. 不是一个很好的类型约束,这是一个遗憾,直观地说,玫瑰树应该是他们存储的元素的花边。

You can fix this by building Rose as itself being the fixpoint of a Bifunctor . 你可以通过构建Rose作为Bifunctor的固定点来解决这个Bifunctor So, in effect, by the time we get to lists, we have three type variables in scope, a , rose and list , and we have functoriality in all of them. 所以,实际上,当我们到达列表时,我们在范围中有三个类型变量, aroselist ,并且我们在所有这些变量中都有functoriality。 You need a different fixpoint type constructor, and a different kit for building Bifunctor instances: for Rose , life gets easier because the a parameter is not used in the inner fixpoint, but in general, to define bifunctors as fixpoints requires trifunctors, and off we go! 你需要一个不同的 fixpoint类型构造函数,以及一个用于构建Bifunctor实例的不同工具包:对于Rose ,生命变得更容易,因为a参数没有在内部修复点中使用,但一般来说,将bifunctors定义为fixpoints需要trifunctors,然后我们关闭走!

This answer of mine shows how to fight the proliferation by showing how indexed types are closed under a fixpoint-of-functor construction. 我的这个答案显示了如何通过展示索引类型在仿函数修复点构造下的关闭来对抗扩散。 That's to say, work not in Type but in i -> Type (for the full variety of index types i ) and you're ready for mutual recursion, GADTs, and so on. 也就是说,不是在Type工作,而是在i -> Type (对于各种索引类型i ),你已准备好进行相互递归,GADT等等。

So, zooming out, rose trees are given by mutual fixpoints, which have a perfectly sensible categorical account, provided you see which categories are actually at work. 因此,放大,玫瑰树是由相互固定点给出的,它们具有完全合理的分类帐户,只要您看到哪些类别实际上在起作用。

This is not really an answer to the question you're asking, but perhaps interesting anyway. 这不是你问的问题的真正答案,但无论如何也许有趣。 Note that with 请注意

Rose a = a * List (Rose a)
List a = 1 + a * List a

and the fact that * distributes over + , you have 以及*分配超过+的事实,你有

  Rose a 
=   {- definition of `Rose` -}
  a * List (Rose a)
=   {- definition of `List` -}
  a * (1 + Rose a * List (Rose a))
=   {- `*` distributes over `+` -}
  a + a * Rose a * List (Rose a)
=   {- `*` is commutative -}
  a + Rose a * a * List (Rose a)
=   {- definition of `Rose` -}
  a + Rose a * Rose a

(the equality really denotes isomorphism). (等式确实表示同构)。 So you might as well have defined 所以你可能已经定义了

Rose a = a + Rose a * Rose a

or in Haskell, 或者在Haskell,

data Rose a = Leaf a | Bin (Rose a) (Rose a)

Which is to say, rose trees are isomorphic to ordinary (leaf-labelled) binary trees, and which clearly form a normal initial algebra. 也就是说,玫瑰树与普通(叶标记)二叉树同构,并且明显形成正常的初始代数。

As you noticed, the definition of the functor for Rose a is trickier due to the fact that the recursive occurrence of the type is fed into a List . 正如您所注意到的, Rose a的仿函数的定义比较复杂,因为类型的递归出现被送入List The problem is that List is itself a recursive type obtained as a fixed point. 问题是List本身是一个作为固定点获得的递归类型。 List (Rose a) basically corresponds to an "arbitrary number of elements of Rose a ", something that you cannot express with a signature of products and sums alone, hence the need for additional abstraction over these multiple recursive points. List (Rose a)基本上对应于“ Rose a任意数量的元素”,这是你无法用产品和总和的签名来表达的东西,因此需要对这些多个递归点进行额外的抽象。

A functor FA - : * -> * will not work, as we would need to find something such that 仿函数FA - : * -> *将不起作用,因为我们需要找到这样的东西

F A X ≃ A × (1 + X × List X)
F A X ≃ A × (1 + X × (1 + X × List X))
F A X ≃ A × (1 + X × (1 + X × (1 + X × List X)))
...

One way to do it is to just treat List as primitive. 一种方法是将List视为原语。 Then Rose a is just the fixed point of 然后Rose a只是固定点

RoseF A : * -> * = λ X . A × List X

Another, more interesting way is to follow the suggestion in the reference you posted, and notice that the type of Rose a can be generalized to abstract over the functor in which the recursive occurrence is fed into 另一个更有趣的方法是遵循您发布的引用中的建议,并注意Rose a的类型可以被推广为抽象的函数,其中递归事件被送入

GRose F A ≃ A × F (GRose F A)

now GRose has type (* -> *) -> (* -> *) , hence it is an higher order functor mapping an endofunctor into another one. 现在GRose有类型(* -> *) -> (* -> *) ,因此它是一个更高阶的函子,将endofunctor映射到另一个。 In our example, it would map the functor List into the type of rose trees. 在我们的示例中,它会将仿函数List映射到玫瑰树的类型。

Notice however that GRose is still recursive, so the above is actually stating an isomorphism rather than a solution to our problem. 但请注意,GRose仍然是递归的,所以上面实际上是说明了同构而不是我们问题的解决方案。 We can try to fix (wink wink) this by additionally abstracting over the recursive point 我们可以尝试通过另外抽象递归点来修复(眨眼)

HRose G F A = A × F (G F A)

Notice that now HRose is a regular higher-order functor of type ((* -> *) -> (* -> *)) -> (* -> *) -> (* -> *) , hence it maps higher-order functors into higher-order functors. 请注意,现在HRose是一个常规的高阶函子,类型为((* -> *) -> (* -> *)) -> (* -> *) -> (* -> *) ,因此它映射得更高将函子编程到高阶函子中。 Computing the least fixed point of HRose gives us 计算HRose的最小固定点给了我们

μ(HRose) F A ≃ A × F (μ(HRose) F A)

So if we put Rose ≡ μ(HRose) List , we get 所以,如果我们把Rose ≡ μ(HRose) List ,我们得到

Rose A ≃ A × List (Rose A)

which is exactly the defining equation for rose trees. 这正是玫瑰树的定义方程。 You can find many further examples of the theory and practice of generic programming using fixed points over higher-order functors. 您可以在高阶仿函数上找到使用固定点的泛型编程理论和实践的更多示例。 Here , for example, Bird and Paterson develop it in the context of nested datatypes (but the definitions clearly hold in general). 这里 ,例如,伯德和帕特森发展它在嵌套数据类型的情况下(但定义明确,一般持有)。 They also show the systematic construction of folds over datatypes defined in such way, as well as various laws. 它们还显示了以这种方式定义的数据类型的折叠的系统构造,以及各种定律。

You seem to understand how this is modelled 你似乎明白这是如何建模的

data List a = Nil | Cons a (List a)

by taking, for any given A , the initial algebra of the endofunctor F(A, -) = 1 + A × (-) . 对于任何给定的A ,取内含子F(A, -) = 1 + A × (-)的初始代数。 Let's call this initial algebra L(A) . 我们称之为初始代数L(A)

If we forget the morphism in L(A) , we can sat that L(A) is an object of our category. 如果我们忘记L(A)的态射,我们可以认为L(A)是我们类别的对象。 Better, L(-) is not only a mapping from objects to objects, but can be seen as an endofunctor. 更好的是, L(-)不仅是从对象到对象的映射,而且可以看作是一个endofunctor。

Once L is seed as an endofunctor, the recursive type 一旦L是种子作为endofunctor,递归类型

data Rose a = Node a (List (Rose a))

is interpreted by taking, for any A m the initial algebra of the functor 通过对于任何A m来解释仿函数的初始代数

G A = A * L A

which is a functor obtained by composing L and * (and the diagonal functor). 这是一个通过组合L* (和对角函子)得到的仿函数。 Hence, the same approach works. 因此,相同的方法有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM