What does “coalgebra” mean in the context of programming?
我在函数编程和PLT循环中已经听过"coalgebras"这个词好几次了,特别是当讨论对象、辅音、透镜等等的时候。谷歌这个术语给出了一些页面,它们给出了这些结构的数学描述,我对此几乎无法理解。有人能解释一下coalgebras在编程环境中的含义,它们的意义是什么,以及它们如何与对象和Comonads相关吗?
代数
我认为,首先要了解代数的概念。这只是代数结构的一个推广,如群、环、单倍体等等。大多数时候,这些东西都是以集合的形式介绍的,但是由于我们是朋友,所以我将讨论haskell类型。(我忍不住用了一些希腊字母,尽管它们让一切看起来更酷!)好的。
那么,代数只是一个具有一些函数和恒等式的
最简单的例子是单倍体。单倍体是任何类型的
所有的函数都在
代数只是数学中被"分解"的一种常见模式,就像我们处理代码一样。人们注意到前面提到的单体、群、格等一大堆有趣的东西都遵循类似的模式,所以他们把它抽象出来。这样做的好处与编程中的一样:它创建了可重用的证明,并使某些类型的推理更容易。好的。F-代数
然而,我们还没有完全完成因子分解。到目前为止,我们有一系列函数
1 2 3 | op ∷ Monoid τ ? Either (τ, τ) () → τ op (Left (a, b)) = mappend (a, b) op (Right ()) = mempty |
对于任何代数,我们实际上都可以重复使用这个转换将所有的
这让我们把代数看作是一个
1 2 | data MonoidArgument τ = Mappend τ τ -- here τ τ is the same as (τ, τ) | Mempty -- here we can just leave the () out |
我们可以对群、环、格以及所有其他可能的结构做同样的事情。好的。
所有这些类型还有什么特别之处?好吧,他们都是
1 2 3 | instance Functor MonoidArgument where fmap f (Mappend τ τ) = Mappend (f τ) (f τ) fmap f Mempty = Mempty |
所以我们可以更广泛地推广我们的代数思想。它只是一些类型的
1 2 |
这通常被称为"F代数",因为它是由函子
现在,希望你能很好地了解代数是什么,它只是普通代数结构的一个推广。那么什么是F-coalgebra?好吧,co意味着它是一个代数的"对偶",也就是说,我们取一个代数并翻转一些箭头。我在上面的定义中只看到一个箭头,所以我将翻转它:好的。
1 2 |
就这样!现在,这个结论似乎有点轻率。它告诉你什么是一个coalgebra,但并不真正给任何洞察它是如何有用或为什么我们关心。一旦我找到或想出一个或两个好的例子,我将在一点后讨论这个问题:p。好的。类和对象
在阅读了一点之后,我想我对如何使用coalgebras来表示类和对象有了一个很好的了解。我们有一个包含类中对象所有可能的内部状态的
如代数例子所示,如果对于任何一个
1 2 | both ∷ τ → (a, b) both x = (f x, g x) |
类型
我们类型
我们需要能够接受参数并修改状态的方法。要做到这一点,我们需要采取所有的论点,并产生一个新的
这里的重要模式是,对象的"方法"和"属性"将对象本身作为第一个参数。这就像python中的
所以让我们把它们放在一起。让我们设想一个具有
1 2 3 4 5 6 7 8 |
我们需要两个部分来表示这个类。首先,我们需要表示对象的内部状态;在这种情况下,它只包含两个
1 2 | data C = Obj { x, y ∷ Int , _name ∷ String } |
我们有两个属性要写。它们非常微不足道:好的。
1 2 3 4 5 | position ∷ C → (Int, Int) position self = (x self, y self) name ∷ C → String name self = _name self |
现在我们只需要更新职位:好的。
1 2 | setPosition ∷ C → (Int, Int) → C setPosition self (newX, newY) = self { x = newX, y = newY } |
这就像一个带有显式
1 2 | coop ∷ C → ((Int, Int), String, (Int, Int) → C) coop self = (position self, name self, setPosition self) |
对于任何
鉴于此,
这允许我们使用coalgebraic推理来处理类。例如,我们可以引入"F-余代数同态"的概念来表示类之间的转换。这是一个听起来吓人的术语,仅仅意味着保留结构的煤壶菌之间的转变。这使得考虑将类映射到其他类变得更加容易。好的。
简而言之,F-coalgebra通过拥有一系列属性和方法来表示类,这些属性和方法都依赖于包含每个对象内部状态的
到目前为止,我们已经讨论过代数和代数作为haskell类型。代数只是一个具有函数
然而,没有什么能真正把这些想法与哈斯克尔联系起来。实际上,它们通常是用集合和数学函数来介绍的,而不是用类型和haskell函数。事实上,我们可以将这些概念推广到任何类别!好的。
对于某些范畴
考虑到其他类别,我们能得到什么?好吧,我们可以在不同的环境中使用相同的想法。像单子一样。在haskell中,monad是一种类型的
1 2 3 |
函子本身形成一个范畴,它们之间的同构就是所谓的"自然变换"。自然变换只是将一个函子变换成另一个函子,同时保留其结构的一种方法。这是一篇很好的文章,有助于解释这个想法。它谈论的是
对于haskell函子,两个函子的组合本身就是一个函子。在伪代码中,我们可以写下:好的。
1 2 | instance (Functor f, Functor g) ? Functor (f ° g) where fmap fun x = fmap (fmap fun) x |
这有助于我们将
现在我们可以把一个单子看作是一个基于一些函子
所以monad就像一个monoid,除了我们有一个函数而不是一个类型。它是同一类代数,只是属于不同的范畴。(据我所知,"一个单子只是内函数类中的一个单倍体"这个短语就是从这里来的。)好的。
现在,我们有两个操作:
1 2 3 |
所以一个辅酶就是一个内功能子。好的。好啊。
F-代数和F-余代数是数学结构,有助于归纳类型(或递归类型)的推理。好的。F-代数
我们先从F-代数开始。我会尽量简单。好的。
我想你知道什么是递归类型。例如,这是整数列表的类型:好的。
1 | data IntList = Nil | Cons (Int, IntList) |
很明显,它是递归的——实际上,它的定义是指它自己。它的定义由两个数据构造函数组成,它们具有以下类型:好的。
1 2 | Nil :: () -> IntList Cons :: (Int, IntList) -> IntList |
注意,我把
如果我们用一种更为固定的理论方法来写这些函数的签名,我们将得到好的。
1 2 | Nil :: 1 -> IntList Cons :: Int × IntList -> IntList |
其中
两套
我们可以"加入"
1 | Nil|Cons :: 1 | (Int × IntList) -> IntList |
实际上,如果
现在考虑另一个数据类型:好的。
1 | data IntTree = Leaf Int | Branch (IntTree, IntTree) |
它有以下构造函数:好的。
1 2 | Leaf :: Int -> IntTree Branch :: (IntTree, IntTree) -> IntTree |
也可加入一个功能:好的。
1 | Leaf|Branch :: Int | (IntTree × IntTree) -> IntTree |
可以看出,这两个
1 | f :: F T -> T |
其中
1 2 | F1 T = 1 | (Int × T) F2 T = Int | (T × T) |
我们可以立即注意到,任何代数类型都可以用这种方式写成。事实上,这就是为什么他们被称为"代数":他们由一些"总和"(工会)和"产品"(交叉产品)的其他类型。好的。
现在我们可以定义f-代数。F-代数只是一对
然后我们可以引入F-代数同态,然后引入初始的F-代数,它们具有非常有用的性质。实际上,
尽管如此,比如说,
1 | foldr (+) 0 [1, 2, 3, 4] -> 1 + 2 + 3 + 4 = 10 |
可以将这种操作推广到任何递归数据类型上。好的。
以下是
1 | foldr :: ((a -> b -> b), b) -> [a] -> b |
注意,我使用大括号将前两个参数与后一个参数分开。这不是真正的
1 | foldr ((+), 0) :: [Int] -> Int |
我们可以看到这是一个函数,它接受一个整数列表并返回一个整数。让我们用我们的
1 2 3 | sumFold :: IntList -> Int sumFold Nil = 0 sumFold (Cons x xs) = x + sumFold xs |
我们看到,该函数由两部分组成:第一部分定义了该函数在
现在假设我们不是在Haskell编程,而是在某些语言中允许在类型签名中直接使用代数类型(技术上,Haskell允许通过元组和EDCOX1×24数据类型使用代数类型,但这将导致不必要的冗长)。考虑一个函数:好的。
1 2 3 | reductor :: () | (Int × Int) -> Int reductor () = 0 reductor (x, s) = x + s |
可以看出,EDCOX1〔25〕是EDCOX1〔26〕类型的函数,正如F代数的定义一样。事实上,对
由于
为了使它更清晰并帮助您看到模式,这里是另一个例子,我们再次从产生的折叠函数开始。考虑将其第一个参数附加到第二个参数的
1 | (append [4, 5, 6]) [1, 2, 3] = (foldr (:) [4, 5, 6]) [1, 2, 3] -> [1, 2, 3, 4, 5, 6] |
在我们的
1 2 3 | appendFold :: IntList -> IntList -> IntList appendFold ys () = ys appendFold ys (Cons x xs) = x : appendFold ys xs |
再来一次,让我们试着写出约简:好的。
1 2 3 | appendReductor :: IntList -> () | (Int × IntList) -> IntList appendReductor ys () = ys appendReductor ys (x, rs) = x : rs |
因此,本质上,F-代数允许我们在递归数据结构上定义"折叠",也就是说,将我们的结构减少到某个值的操作。好的。F-余代数
F-余代数是F-代数的所谓"对偶"项。它们允许我们为递归数据类型定义
假设您有以下类型:好的。
1 | data IntStream = Cons (Int, IntStream) |
这是一个无限的整数流。它唯一的构造函数具有以下类型:好的。
1 | Cons :: (Int, IntStream) -> IntStream |
或者,就集合而言好的。
1 | Cons :: Int × IntStream -> IntStream |
haskell允许您在数据构造函数上进行模式匹配,因此您可以在
1 2 3 4 5 | head :: IntStream -> Int head (Cons (x, xs)) = x tail :: IntStream -> IntStream tail (Cons (x, xs)) = xs |
您可以自然地将这些函数"连接"到
1 2 | head&tail :: IntStream -> Int × IntStream head&tail (Cons (x, xs)) = (x, xs) |
注意函数的结果如何与我们的
1 | g :: T -> F T |
其中
1 | F1 T = Int × T |
现在,f-coalgebra是一对
在所有的F-余代数中,有所谓的末端F-余代数,它是初始F-代数的对偶。例如,
考虑以下函数,它从给定的整数开始生成一个连续整数流:好的。
1 2 | nats :: Int -> IntStream nats n = Cons (n, nats (n+1)) |
现在让我们检查一个函数
1 2 | natsBuilder :: Int -> Int × Int natsBuilder n = (n, n+1) |
同样,我们可以看到
另一个例子是一个函数,它取一个值和一个函数,并将函数的连续应用流返回到值:好的。
1 2 | iterate :: (Int -> Int) -> Int -> IntStream iterate f n = Cons (n, iterate f (f n)) |
其建设者功能如下:好的。
1 2 | iterateBuilder :: (Int -> Int) -> Int -> Int × Int iterateBuilder f n = (n, f n) |
因此,简而言之,F-代数允许定义折叠,也就是说,将递归结构简化为单个值的操作,而F-代数允许做相反的操作:从单个值构造[潜在的]无限结构。好的。
事实上,在哈斯克尔F-代数和F-余代数中是一致的。这是一个非常好的属性,它是每种类型中存在"bottom"值的结果。因此,在haskell中,可以为每种递归类型创建折叠和展开。然而,这背后的理论模型比我上面介绍的要复杂,所以我故意避免了它。好的。
希望这有帮助。好的。好啊。
通过这篇教程文章,你应该了解一下计算机科学中的协代数。
下面是一个引证,让你相信,
In general terms, a program in some programming language manipulates data. During the
development of computer science over the past few decades it became clear that an abstract
description of these data is desirable, for example to ensure that one's program does not depend on the particular representation of the data on which it operates. Also, such abstractness facilitates correctness proofs.
This desire led to the use of algebraic methods in computer science, in a branch called algebraic specification or abstract data type theory. The object of study are data types in themselves, using notions of techniques which are familiar from algebra. The data types used by computer scientists are often generated from a given collection of (constructor) operations,and it is for this reason that"initiality" of algebras plays such an important role.
Standard algebraic techniques have proved useful in capturing various essential aspects of data structures used in computer science. But it turned out to be difficult to algebraically describe some of the inherently dynamical structures occurring in computing. Such structures usually involve a notion of state, which can be transformed in various ways. Formal approaches to such state-based dynamical systems generally make use of automata or transition systems, as classical early references.
During the last decade the insight gradually grew that such state-based systems should not be described as algebras, but as so-called co-algebras. These are the formal dual of algebras, in a way which will be made precise in this tutorial. The dual property of"initiality" for algebras, namely finality turned out to be crucial for such co-algebras. And the logical reasoning principle that is needed for such final co-algebras is not induction but co-induction.
序言,关于范畴理论。范畴理论应重命名函子理论。范畴是人们必须定义的,以定义函子。(此外,函子是人们必须定义的,以便定义自然的变换。)
什么是函子?它是从一个集合到另一个集合的转换,保留了它们的结构。(关于更多细节,网上有很多很好的描述)。
什么是F-代数?它是函子的代数。它只是研究函数的普适性。
它如何能与计算机科学联系起来?程序可以作为一组结构化的信息来查看。程序的执行对应于对这个结构化信息集的修改。执行应该保留程序结构听起来不错。然后,可以将执行视为对这组信息使用函数。(定义程序的那个)。
为什么是F-代数?程序本质上是双重的,因为它们是由信息来描述的,并且是根据信息来行动的。然后主要对组成程序并使其改变的信息进行双向查看。
- 可以定义为程序正在处理的信息的数据。
- 可以定义为程序共享的信息的状态。
在这个阶段,我想说,
- F-代数是研究作用于数据宇宙的函数变换(如这里所定义的)。
- f-co-代数是对作用于状态宇宙的函数变换的研究。
在一个程序的生命周期中,数据和状态共存,并且它们互相完成。它们是双重的。
我先从一些与编程相关的东西开始,然后添加一些数学的东西,让它尽可能地具体化和脚踏实地。
让我们引用一些计算机科学家关于共诱导…http://www. cas.UMD.EdU/~MixSky/Posi/2012-09/04-on理解CONTIFIONE.HTML.
Induction is about finite data, co-induction is about infinite data.
The typical example of infinite data is the type of a lazy list (a
stream). For example, lets say that we have the following object in
memory:
1 2 | let (pi : int list) = (* some function which computes the digits of π. *) |
The computer can’t hold all of π, because it only has a finite amount
of memory! But what it can do is hold a finite program, which will
produce any arbitrarily long expansion of π that you desire. As long
as you only use finite pieces of the list, you can compute with that
infinite list as much as you need.However, consider the following program:
1 2 3 4 5 |
This program should print the
third digit of pi. But in some languages, any argument to a function is evaluated before being passed
into a function (strict, not lazy, evaluation). If we use this
reduction order, then our above program will run forever computing the
digits of pi before it can be passed to our printer function (which
never happens). Since the machine does not have infinite memory, the
program will eventually run out of memory and crash. This might not be the best evaluation order.
HTTP://ADAM.CHLLPAL.NET/CPDT/HTML/CONTIOVIEW.HTML.
In lazy functional programming languages like Haskell, infinite data structures
are everywhere. Infinite lists and more exotic datatypes provide convenient
abstractions for communication between parts of a program. Achieving similar
convenience without infinite lazy structures would, in many cases, require
acrobatic inversions of control flow.
http://www.alexandrasilva.org//talks.html
将环境数学上下文与通常的编程任务相关联什么是"代数"?代数结构通常看起来像:
这听起来应该像是带有1的对象。属性和2。方法。或者更好的是,它听起来应该像类型签名。
标准的数学例子包括monoid?小组?矢量空间?"代数。单倍体就像自动机:动词序列(如
组包含可以相加或相减的内容。例如,可以将
Algebras?向量空间还可以做另一件事:有一些
(这就是人们关注(范畴论)普遍属性的原因:告诉他们乘法应该做什么或应该是什么样子的:
)
代数→代数与乘法相比,用一种感觉不武断的方式来定义Comultiplication更容易,因为从
counit通常是跟踪(对角线项的总和),但重要的是你的counit所做的;
一般来说,看到双重空间的原因是因为在这个空间中思考更容易。例如,有时考虑法向量比考虑它的法向量更容易,但是你可以用向量控制平面(包括超平面)(现在我说的是熟悉的几何向量,就像在光线跟踪器中一样)。
驯服(非)结构化数据数学家们可能正在为一些有趣的东西建模,比如TQFT,而程序员则不得不与之抗衡。
- 日期/时间(
+ :: (Date,Duration) → Date ) - 地点(
Paris ≠(+48.8567,+2.3508) !这是一个形状,不是一个点。) - 非结构化JSON,在某种意义上应该是一致的,
- 错误但关闭XML,
- 极其复杂的地理信息系统数据,应满足合理关系的负荷,
- 正则表达式对您有意义,但对Perl的意义要小得多。
- 客户关系管理系统,应包含所有行政人员的电话号码和别墅位置、他(现在的前)妻子和孩子的姓名、生日和所有以前的礼物,每个礼物都应满足"明显"的关系(对客户来说是显而易见的),这些关系极难编码。
- …
计算机科学家在谈论代数时,通常都会想到一些操作,比如笛卡尔产品。我相信这就是人们所说的"代数是哈斯克尔的余代数"的意思。但是,在一定程度上,程序员必须对复杂的数据类型(如