Folding infinite list through F-algebra

Folding infinite list through F-algebra

嘛,在知乎写的第一篇文章,参考了很多现有文章的格式及表达思路www。主要目的还是讨论一些F-algebra的概念和锻炼自己的表达能力。起因是最近刷到codewars上一道题:Folding through a fixed point,其中涉及到一些以前接触过的F-algebra的概念,但题中并没有详细解释清楚F-algebra,评论区存在做完题但没明白到底发生了啥的评论。我也是在codewars上写了一个简单的解释,这里在不剧透题目的情况下搬运一些自己写的东西,并给出一个实际的例子:constructing, folding infinite list

注意:本文中存在一些伪代码,主要是类型签名用来说明类型。

前置知识

  • 对范畴论的一个大概的认识会有很大帮助
  • Haskell的基础语法及Functor typeclass的使用
  • 对交换图表的相关知识

什么是Algebra

简单来说,Algebra可以理解为能以某种方式组织表达式,以及能以某种方式对表达式进行规约的结构。这里我们所关心的表达式是其中的一种:recursively defined的表达式。比如,在Haskell中我们可以通过如下语法来组织一个列表:

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

接着,我们可以通过value constructor构造我们需要的列表,通过pattern matching以我们想要的方式对列表进行规约。

list :: List String
list = Cons "9" $ Cons "9" $ Cons "6" $ Cons "1" $ Nil

stringify :: List String -> String
stringify Nil = ""
stringify (Cons x xs) = x ++ stringify xs

result :: String
result = stringify list

非递归的定义我们的List

我们可以将List中的递归定义提出来,增加一个Type constructor的参数:

data ListF a l = Nil | Cons a l

哪如何通过这个type来重新定义刚才的List呢?我们通过fixed point来实现,这里我们所选用的fixed point的定义least fixed point和greatest fixed point相等,且能直观的表现我们的意图:将非递归的type变成递归的。

newtype Fix f = In (f (Fix f))

这样,我们可以重新定义我们的List:

type List a = Fix (ListF a)

list :: List String
list = In $ Cons "9" $ In $ Cons "9" $ In $ Cons "6" $ In $ Cons "1" $ In Nil

一个F-Algebra的定义是什么

不同代数结构会存在共有的特性,在猫论中,F-Algebra可以用来generalize algebraic structure,F-Algebra可以用来表示各种数据结构。具体的来说C是一个category,F: C → C是一个endofunctor,X是C中一个object,alg是C中F X → X的一个态射,那么F-Algebra就是一个(X, alg)的二元组。

对应到Haskell中则是在Hask范畴,f是一个Functor typeclass的instance,x是一个type,alg的类型则是:

alg :: f x -> x

一个F-Coalgebra的定义是什么

既然名字里带co了,那么很容易想到是F-Algebra的dual。具体来说C是一个category,F: C → C是一个endofunctor,X是C中一个object,coalg是C中X → F X的一个态射,那么F-Coalgebra就是一个(X, coalg)的二元组。

对应到Haskell中则是在Hask范畴,f是一个Functor typeclass的instance,x是一个type,coalg的类型则是:

coalg :: x -> f x

理解Algebra间的态射

根据不同的x的type,我们可以写出不同的algebra,猫论是一个研究不同东西间相同结构的理论,这里,我们要找出相同Functor间不同algebra的关系。

如果我们有以下两个type A和B,以及其algebra:

data A = A
newtype B = B A

mkA :: B -> A
mkA (B a) = a
mkB :: A -> B
mkB a = (B a)

algA :: F A -> A
algB :: F B -> B

coalgA :: A -> F A
coalgB :: B -> F B

那么我们有如下所示的态射关系,我们以Commutative Diagram表示:

\begin{equation} \tag{F-Algebra} \begin{CD} {F A} @>{fmap \ mkB}>> {F B} \\ @V{algA}VV @VV{algB}V \\ {A} @>{mkB}>> {B} \\ \end{CD} \end{equation} \begin{equation} \tag{F-Coalgebra} \begin{CD} {F A} @<{fmap \ mkA}<< {F B} \\ @A{coalgA}AA @AA{coalgB}A \\ {A} @<{mkA}<< {B} \\ \end{CD} \end{equation}

Initial F-Algebra和Terminal F-Coalgebra

Initial F-Algebra和Terminal F-Coalgebra是一类具有特殊性质的F-Algebra和F-Coalgebra。对于一个endofunctor F: C → C来说,存在从其Initial F-Algebra到其他所有F-Algebra的唯一态射,存在从其他所有F-Coalgebra到其Terminal F-Coalgebra的唯一态射。我们将其分别称之为catamorphism和anamorphism。

这里,我们的不动点Fix的In :: f (Fix f) -> Fix f就是Initial F-Algebra(和Terminal F-Coalgebra),根据Lambek’s Lemma我们可以知道存在unIn :: Fix f -> f (Fix f),我们也很容易将其写出来,存在非常简单的isomorphism:

In :: f (Fix f) -> Fix f
unIn :: Fix f -> f (Fix f)

newtype Fix f = In (f (Fix f))
unIn (In x) = x

那么如果我们构造出g和cog:

g :: Fix f -> x
cog :: x -> Fix f

我们就可以很方便的将一个用Fix表示的代数表达式规约成我们想要的形式(或者反过来),我们用Commutative Diagram表示出来:

\begin{equation} \tag{Initial F-Algebra} \begin{CD} {F (Fix \ F)} @>{fmap \ g}>> {F A} \\ @V{In}VV @VV{algA}V \\ {Fix \ F} @>{g}>> {A} \\ \end{CD} \end{equation} \begin{equation} \tag{Terminal F-Coalgebra} \begin{CD} {F (Fix \ F)} @<{fmap \ cog}<< {F A} \\ @A{unIn}AA @AA{coalgA}A \\ {Fix \ F} @<{cog}<< {A} \\ \end{CD} \end{equation}

构造catamorphism与anamorphism

根据上面的Commutative Diagram,我们可以轻易发现,简单的将In和unIn交换,我们就能轻易从特定F-Algebra、F-Coalgebra构造出g和cog:

\begin{equation} \tag{Initial F-Algebra} \begin{CD} {F (Fix \ F)} @>{fmap \ g}>> {F A} \\ @A{unIn}AA @VV{algA}V \\ {Fix \ F} @>{g}>> {A} \\ \end{CD} \end{equation} \begin{equation} \tag{Terminal F-Coalgebra} \begin{CD} {F (Fix \ F)} @<{fmap \ cog}<< {F A} \\ @V{In}VV @AA{coalgA}A \\ {Fix \ F} @<{cog}<< {A} \\ \end{CD} \end{equation}

我们将其用Haskell表示出来:

cata :: Functor f => (f x -> x) -> (Fix f -> x)
cata alg = alg . fmap (cata alg) . unIn

ana :: Functor f => (x -> f x) -> (x -> Fix f)
ana coalg = In . fmap (ana coalg) . coalg

Folding infinite list through F-algebra

我们可以用F-Coalgebra来构造Infinite data structures,并使用F-algebra对其进行fold。例如我们的List:

data ListF a l = Nil | Cons a l

很容易将其Functor instance写出来:

instance Functor (ListF a) where
    fmap f Nil = Nil
    fmap f (Cons a l) = Cons a (f l)

这里我们定义这样一个Coalgebra:

coalg :: Int -> ListF Int Int
coalg x = Cons x (x + 1)

我们通过这个Coalgebra和Anamorphism定义一个Infinite的自增List:

list :: Fix (ListF Int)
list = ana coalg 256

接着,我们定义一个可以从Infinite的List中规约出值的Algebra:

alg :: ListF Int Int -> Int
alg Nil = 0
alg (Cons a b) = if a < 1024 then a + b else a

接着我们使用Catamorphism和这个Algebra规约出我们表达式的值:

result :: Int
result = cata alg list

这里跟foldr很相似,只要我们规约Infinite的List时某次没有使用Cons里b的的值,那么规约就能终止。

结束

很久没写过这种类型的文章了,难免出现码字错误或者表述不清。有什么问题评论区轻喷/w\

文章被以下专栏收录
14 条评论
推荐阅读