scala 在编程的上下文中,“代数”是什么意思?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16015020/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What does "coalgebra" mean in the context of programming?
提问by missingfaktor
I have heard the term "coalgebras" several times in functional programming and PLT circles, especially when the discussion is about objects, comonads, lenses, and such. Googling this term gives pages that give mathematical description of these structures which is pretty much incomprehensible to me. Can anyone please explain what coalgebras mean in the context of programming, what is their significance, and how they relate to objects and comonads?
我在函数式编程和 PLT 圈子中多次听到“代数”这个词,特别是当讨论是关于对象、共子、透镜等的时候。谷歌搜索这个术语给出了对这些结构进行数学描述的页面,这对我来说几乎是不可理解的。任何人都可以解释代数在编程上下文中的含义,它们的意义是什么,以及它们与对象和共子的关系吗?
回答by Tikhon Jelvis
Algebras
代数
I think the place to start would be to understand the idea of an algebra. This is just a generalization of algebraic structures like groups, rings, monoids and so on. Most of the time, these things are introduced in terms of sets, but since we're among friends, I'll talk about Haskell types instead. (I can't resist using some Greek letters though—they make everything look cooler!)
我认为首先要了解代数的概念。这只是对群、环、幺半群等代数结构的概括。大多数时候,这些东西都是以集合的形式介绍的,但由于我们是朋友,所以我将改为讨论 Haskell 类型。(虽然我无法抗拒使用一些希腊字母 - 它们让一切看起来更酷!)
An algebra, then, is just a type τwith some functions and identities. These functions take differing numbers of arguments of type τand produce a τ: uncurried, they all look like (τ, τ,…, τ) → τ. They can also have "identities"—elements of τthat have special behavior with some of the functions.
因此,代数只是一种τ具有某些函数和恒等式的类型。这些函数采用不同数量的类型参数τ并产生一个τ: 非柯里化的,它们看起来都像(τ, τ,…, τ) → τ. 它们还可以具有“身份”——其中的元素对τ某些功能具有特殊行为。
The simplest example of this is the monoid. A monoid is any type τwith a function mappend ∷ (τ, τ) → τand an identity mzero ∷ τ. Other examples include things like groups (which are just like monoids except with an extra invert ∷ τ → τfunction), rings, lattices and so on.
最简单的例子是幺半群。幺半群是τ具有函数mappend ∷ (τ, τ) → τ和身份的任何类型mzero ∷ τ。其他例子包括群(就像幺半群一样,除了有一个额外的invert ∷ τ → τ功能)、环、格等等。
All the functions operate on τbut can have different arities. We can write these out as τ? → τ, where τ?maps to a tuple of nτ. This way, it makes sense to think of identities as τ? → τwhere τ?is just the empty tuple (). So we can actually simplify the idea of an algebra now: it's just some type with some number of functions on it.
所有函数都在操作,τ但可以有不同的参数。我们可以将这些写成τ? → τ,其中τ?映射到 的元组nτ。这样,将身份视为τ? → τwhere τ?is just the empty tuple 是有意义的()。所以我们现在实际上可以简化代数的概念:它只是一种带有一定数量函数的类型。
An algebra is just a common pattern in mathematics that's been "factored out", just like we do with code. People noticed that a whole bunch of interesting things—the aforementioned monoids, groups, lattices and so on—all follow a similar pattern, so they abstracted it out. The advantage of doing this is the same as in programming: it creates reusable proofs and makes certain kinds of reasoning easier.
代数只是数学中一种被“分解”出来的常见模式,就像我们处理代码一样。人们注意到一大堆有趣的东西——前面提到的幺半群、群、格等等——都遵循类似的模式,所以他们把它抽象出来。这样做的好处与在编程中相同:它创建可重用的证明并使某些类型的推理更容易。
F-Algebras
F-代数
However, we're not quite done with factoring. So far, we have a bunch of functions τ? → τ. We can actually do a neat trick to combine them all into one function. In particular, let's look at monoids: we have mappend ∷ (τ, τ) → τand mempty ∷ () → τ. We can turn these into a single function using a sum type—Either. It would look like this:
然而,我们还没有完成因式分解。到目前为止,我们有一堆函数τ? → τ。我们实际上可以做一个巧妙的技巧将它们全部组合成一个函数。特别是,让我们看看幺半群:我们有mappend ∷ (τ, τ) → τ和mempty ∷ () → τ。我们可以使用 sum 类型将这些转换为单个函数—— Either。它看起来像这样:
op ∷ Monoid τ ? Either (τ, τ) () → τ
op (Left (a, b)) = mappend (a, b)
op (Right ()) = mempty
We can actually use this transformation repeatedly to combine allthe τ? → τfunctions into a single one, for anyalgebra. (In fact, we can do this for any number of functions a → τ, b → τand so on for anya, b,….)
对于任何代数,我们实际上可以反复使用这种转换来将所有τ? → τ函数组合成一个单一的函数。(事实上,我们可以为任何数量的功能做到这一点,等了所有。)a → τb → τa, b,…
This lets us talk about algebras as a type τwith a singlefunction from some mess of Eithers to a single τ. For monoids, this mess is: Either (τ, τ) (); for groups (which have an extra τ → τoperation), it's: Either (Either (τ, τ) τ) (). It's a different type for every different structure. So what do all these types have in common? The most obvious thing is that they are all just sums of products—algebraic data types. For example, for monoids, we could create a monoid argument type that works for anymonoid τ:
这让我们可以将代数视为τ具有单个函数的类型,从一些混乱的Eithers 到单个τ. 对于幺,这个烂摊子是:Either (τ, τ) (); 对于组(有一个额外的τ → τ操作),它是:Either (Either (τ, τ) τ) (). 对于每种不同的结构,它都是不同的类型。那么所有这些类型有什么共同点呢?最明显的是,它们都只是乘积的总和——代数数据类型。例如,对于幺半群,我们可以创建一个适用于任何幺半群 τ的幺半群参数类型:
data MonoidArgument τ = Mappend τ τ -- here τ τ is the same as (τ, τ)
| Mempty -- here we can just leave the () out
We can do the same thing for groups and rings and lattices and all the other possible structures.
我们可以对群、环、格子和所有其他可能的结构做同样的事情。
What else is special about all these types? Well, they're all Functors! E.g.:
所有这些类型还有什么特别之处?嗯,他们都是Functors!例如:
instance Functor MonoidArgument where
fmap f (Mappend τ τ) = Mappend (f τ) (f τ)
fmap f Mempty = Mempty
So we can generalize our idea of an algebra even more. It's just some type τwith a function f τ → τfor some functor f. In fact, we could write this out as a typeclass:
所以我们可以更加概括我们的代数概念。它只是一些类型,τ带有f τ → τ一些 functor的功能f。事实上,我们可以把它写成一个类型类:
class Functor f ? Algebra f τ where
op ∷ f τ → τ
This is often called an "F-algebra" because it's determined by the functor F. If we could partially apply typeclasses, we could define something like class Monoid = Algebra MonoidArgument.
这通常称为“F 代数”,因为它由函子确定F。如果我们可以部分应用类型类,我们可以定义类似class Monoid = Algebra MonoidArgument.
Coalgebras
代数
Now, hopefully you have a good grasp of what an algebra is and how it's just a generalization of normal algebraic structures. So what is an F-coalgebra? Well, the co implies that it's the "dual" of an algebra—that is, we take an algebra and flip some arrows. I only see one arrow in the above definition, so I'll just flip that:
现在,希望您已经很好地掌握了代数是什么以及它如何只是正常代数结构的概括。那么什么是 F 代数呢?好吧,co 暗示它是代数的“对偶”——也就是说,我们取一个代数并翻转一些箭头。我在上面的定义中只看到一个箭头,所以我将翻转它:
class Functor f ? CoAlgebra f τ where
coop ∷ τ → f τ
And that's all it is! Now, this conclusion may seem a little flippant (heh). It tells you whata coalgebra is, but does not really give any insight on how it's useful or why we care. I'll get to that in a bit, once I find or come up with a good example or two :P.
仅此而已!现在,这个结论似乎有点轻率(呵呵)。它告诉你什么是余代数,但并没有真正提供关于它如何有用或我们为什么关心的任何见解。一旦我找到或想出一个或两个很好的例子,我会很快解决这个问题:P。
Classes and Objects
类和对象
After reading around a bit, I think I have a good idea of how to use coalgebras to represent classes and objects. We have a type Cthat contains all the possible internal states of objects in the class; the class itself is a coalgebra over Cwhich specifies the methods and properties of the objects.
在阅读了一些之后,我想我对如何使用余代数来表示类和对象有了很好的了解。我们有一个类型C,它包含类中对象的所有可能的内部状态;类本身是一个代数,C它指定了对象的方法和属性。
As shown in the algebra example, if we have a bunch of functions like a → τand b → τfor any a, b,…, we can combine them all into a single function using Either, a sum type. The dual "notion" would be combining a bunch of functions of type τ → a, τ → band so on. We can do this using the dual of a sum type—a product type. So given the two functions above (called fand g), we can create a single one like so:
如该代数的示例所示,如果我们有一堆相同功能a → τ和b → τ任何a, b,…中,我们可以将它们组合成的所有使用单个函数Either,总和类型。双重“概念”将组合一系列类型的函数τ → a,τ → b依此类推。我们可以使用 sum 类型的对偶(乘积类型)来做到这一点。因此,鉴于上面的两个函数(称为f和g),我们可以像这样创建一个:
both ∷ τ → (a, b)
both x = (f x, g x)
The type (a, a)is a functor in the straightforward way, so it certainly fits with our notion of an F-coalgebra. This particular trick lets us package up a bunch of different functions—or, for OOP, methods—into a single function of type τ → f τ.
类型(a, a)是一个简单的函子,所以它当然符合我们的 F-coalgebra 概念。这个特殊的技巧让我们可以将一堆不同的函数——或者,对于 OOP,方法——打包成一个类型为 的函数τ → f τ。
The elements of our type Crepresent the internalstate of the object. If the object has some readable properties, they have to be able to depend on the state. The most obvious way to do this is to make them a function of C. So if we want a length property (e.g. object.length), we would have a function C → Int.
我们类型的元素C代表对象的内部状态。如果对象具有一些可读属性,则它们必须能够依赖于状态。最明显的方法是使它们成为C. 所以如果我们想要一个长度属性(例如object.length),我们会有一个函数C → Int。
We want methods that can take an argument and modify state. To do this, we need to take all the arguments and produce a new C. Let's imagine a setPositionmethod which takes an xand a ycoordinate: object.setPosition(1, 2). It would look like this: C → ((Int, Int) → C).
我们想要可以接受参数并修改状态的方法。为此,我们需要获取所有参数并生成一个新的C. 让我们想象setPosition这需要一种方法x和y协调:object.setPosition(1, 2)。它是这样的:C → ((Int, Int) → C)。
The important pattern here is that the "methods" and "properties" of the object take the object itself as their first argument. This is just like the selfparameter in Python and like the implicit thisof many other languages. A coalgebra essentially just encapsulates the behavior of taking a selfparameter: that's what the first Cin C → F Cis.
这里的重要模式是对象的“方法”和“属性”将对象本身作为它们的第一个参数。这就像selfPython 中的参数以及this许多其他语言的隐式参数。余代数本质上只是封装了接受self参数的行为:这就是第一个Cin C → F C。
So let's put it all together. Let's imagine a class with a positionproperty, a nameproperty and setPositionfunction:
所以让我们把它们放在一起。让我们想象一个具有position属性、name属性和setPosition函数的类:
class C
private
x, y : Int
_name : String
public
name : String
position : (Int, Int)
setPosition : (Int, Int) → C
We need two parts to represent this class. First, we need to represent the internal state of the object; in this case it just holds two Ints and a String. (This is our type C.) Then we need to come up with the coalgebra representing the class.
我们需要两部分来表示这个类。首先,我们需要表示对象的内部状态;在这种情况下,它只包含两个Ints 和 a String。(这是我们的类型C。)然后我们需要想出代表类的余代数。
data C = Obj { x, y ∷ Int
, _name ∷ String }
We have two properties to write. They're pretty trivial:
我们有两个属性要写。它们非常简单:
position ∷ C → (Int, Int)
position self = (x self, y self)
name ∷ C → String
name self = _name self
Now we just need to be able to update the position:
现在我们只需要能够更新位置:
setPosition ∷ C → (Int, Int) → C
setPosition self (newX, newY) = self { x = newX, y = newY }
This is just like a Python class with its explicit selfvariables. Now that we have a bunch of self →functions, we need to combine them into a single function for the coalgebra. We can do this with a simple tuple:
这就像一个带有显式self变量的 Python 类。现在我们有一堆self →函数,我们需要将它们组合成一个单一的函数来实现余代数。我们可以用一个简单的元组来做到这一点:
coop ∷ C → ((Int, Int), String, (Int, Int) → C)
coop self = (position self, name self, setPosition self)
The type ((Int, Int), String, (Int, Int) → c)—for anyc—is a functor, so coopdoes have the form we want: Functor f ? C → f C.
类型((Int, Int), String, (Int, Int) → c)-对于任何c-is算符,所以coop确实有我们想要的形式:Functor f ? C → f C。
Given this, Calong with coopform a coalgebra which specifies the class I gave above. You can see how we can use this same technique to specify any number of methods and properties for our objects to have.
鉴于此,C连同coop指定我在上面给出的类的余代数。您可以看到我们如何使用相同的技术为我们的对象指定任意数量的方法和属性。
This lets us use coalgebraic reasoning to deal with classes. For example, we can bring in the notion of an "F-coalgebra homomorphism" to represent transformations between classes. This is a scary sounding term that just means a transformation between coalgebras that preserves structure. This makes it much easier to think about mapping classes onto other classes.
这让我们可以使用代数推理来处理类。例如,我们可以引入“F-coalgebra 同态”的概念来表示类之间的转换。这是一个听起来很可怕的术语,仅表示保留结构的代数之间的转换。这使得考虑将类映射到其他类变得更加容易。
In short, an F-coalgebra represents a class by having a bunch of properties and methods that all depend on a selfparameter containing each object's internal state.
简而言之,F-coalgebra 通过拥有一堆属性和方法来表示一个类,这些属性和方法都依赖于self包含每个对象内部状态的参数。
Other Categories
其他类别
So far, we've talked about algebras and coalgebras as Haskell types. An algebra is just a type τwith a function f τ → τand a coalgebra is just a type τwith a function τ → f τ.
到目前为止,我们已经将代数和余代数作为 Haskell 类型进行了讨论。代数只是一种τ带有函数的类型,f τ → τ而代数只是一种τ带有函数的类型τ → f τ。
However, nothing really ties these ideas to Haskell per se. In fact, they're usually introduced in terms of sets and mathematical functions rather than types and Haskell functions. Indeed,we can generalize these concepts to anycategories!
然而,没有什么能真正将这些想法与 Haskell本身联系起来。事实上,它们通常是根据集合和数学函数而不是类型和 Haskell 函数来介绍的。事实上,我们可以将这些概念推广到任何类别!
We can define an F-algebra for some category C. First, we need a functor F : C → C—that is, an endofunctor. (All Haskell Functors are actually endofunctors from Hask → Hask.) Then, an algebra is just an object Afrom Cwith a morphism F A → A. A coalgebra is the same except with A → F A.
我们可以为某个类别定义一个 F 代数C。首先,我们需要一个函子F : C → C,也就是说,一个endofunctor。(所有 HaskellFunctor实际上都是来自 的内函子Hask → Hask。)那么,代数只是A来自C具有态射的对象F A → A。余代数是相同的,除了A → F A。
What do we gain by considering other categories? Well, we can use the same ideas in different contexts. Like monads. In Haskell, a monad is some type M ∷ ★ → ★with three operations:
考虑其他类别我们会得到什么?好吧,我们可以在不同的上下文中使用相同的想法。就像单子一样。在 Haskell 中,monad 是M ∷ ★ → ★具有三种操作的某种类型:
map ∷ (α → β) → (M α → M β)
return ∷ α → M α
join ∷ M (M α) → M α
The mapfunction is just a proof of the fact that Mis a Functor. So we can say that a monad is just a functor with twooperations: returnand join.
该map功能只是一个事实,证明M是一个Functor。所以我们可以说 monad 只是一个具有两个操作的函子:return和join。
Functors form a category themselves, with morphisms between them being so-called "natural transformations". A natural transformation is just a way to transform one functor into another while preserving its structure. Here'sa nice article helping explain the idea. It talks about concat, which is just joinfor lists.
函子本身形成一个范畴,它们之间的态射是所谓的“自然变换”。自然变换只是将一个函子变换为另一个同时保留其结构的一种方式。这是一篇很好的文章,可以帮助解释这个想法。它谈到concat,这仅join适用于列表。
With Haskell functors, the composition of two functors is a functor itself. In pseudocode, we could write this:
使用 Haskell 函子,两个函子的组合就是一个函子本身。在伪代码中,我们可以这样写:
instance (Functor f, Functor g) ? Functor (f ° g) where
fmap fun x = fmap (fmap fun) x
This helps us think about joinas a mapping from f ° f → f. The type of joinis ?α. f (f α) → f α. Intuitively, we can see how a function valid for alltypes αcan be thought of as a transformation of f.
这有助于我们将其join视为来自 的映射f ° f → f。的类型join是?α. f (f α) → f α。直观地,我们可以看到如何将一个对所有类型α都有效的函数视为 的转换f。
returnis a similar transformation. Its type is ?α. α → f α. This looks different—the first αis not "in" a functor! Happily, we can fix this by adding an identity functor there: ?α. Identity α → f α. So returnis a transformation Identity → f.
return是类似的变换。它的类型是?α. α → f α. 这看起来不同——第一个α不在函子中!令人高兴的是,我们可以通过添加一个身份函子有解决这个问题:?α. Identity α → f α。return转型也是如此Identity → f。
Now we can think about a monad as just an algebra based around some functor fwith operations f ° f → fand Identity → f. Doesn't this look familiar? It's very similar to a monoid, which was just some type τwith operations τ × τ → τand () → τ.
现在我们可以将 monad 看作只是基于一些f带有操作f ° f → f和 的函子的代数Identity → f。这看起来是不是很熟悉?它非常类似于幺半群,它只是一些τ带有操作τ × τ → τ和的类型() → τ。
So a monad is just like a monoid, except instead of having a type we have a functor. It's the same sort of algebra, just in a different category. (This is where the phrase "A monad is just a monoid in the category of endofunctors" comes from as far as I know.)
所以一个 monad 就像一个幺半群,除了我们有一个函子而不是一个类型。这是同一种代数,只是属于不同的类别。(据我所知,这就是短语“单子只是内函子范畴中的幺半群”的来源。)
Now, we have these two operations: f ° f → fand Identity → f. To get the corresponding coalgebra, we just flip the arrows. This gives us two new operations: f → f ° fand f → Identity. We can turn them into Haskell types by adding type variables as above, giving us ?α. f α → f (f α)and ?α. f α → α. This looks just like the definition of a comonad:
现在,我们有这两个操作:f ° f → f和Identity → f。为了得到相应的余代数,我们只需翻转箭头即可。这给了我们两个新的操作:f → f ° f和f → Identity。我们可以通过添加上面的类型变量将它们转换为 Haskell 类型,给我们?α. f α → f (f α)和?α. f α → α。这看起来就像一个 comonad 的定义:
class Functor f ? Comonad f where
coreturn ∷ f α → α
cojoin ∷ f α → f (f α)
So a comonad is then a coalgebrain a category of endofunctors.
因此,共谐子是一类内函子中的余代数。
回答by Vladimir Matveev
F-algebras and F-coalgebras are mathematical structures which are instrumental in reasoning about inductive types(or recursive types).
F-algebras 和 F-coalgebras 是数学结构,它们有助于推理归纳类型(或递归类型)。
F-algebras
F-代数
We'll start first with F-algebras. I will try to be as simple as possible.
我们将首先从 F 代数开始。我会尽量简单。
I guess you know what is a recursive type. For example, this is a type for a list of integers:
我想你知道什么是递归类型。例如,这是整数列表的类型:
data IntList = Nil | Cons (Int, IntList)
It is obvious that it is recursive - indeed, its definition refers to itself. Its definition consists of two data constructors, which have the following types:
很明显,它是递归的——事实上,它的定义是指它自己。它的定义由两个数据构造函数组成,它们有以下类型:
Nil :: () -> IntList
Cons :: (Int, IntList) -> IntList
Note that I have written type of Nilas () -> IntList, not simply IntList. These are in fact equivalent types from the theoretical point of view, because ()type has only one inhabitant.
请注意,我写的是Nilas类型,而() -> IntList不是简单的IntList. 从理论的角度来看,这些实际上是等效的类型,因为()类型只有一个居民。
If we write signatures of these functions in a more set-theoretical way, we will get
如果我们以更集合论的方式编写这些函数的签名,我们将得到
Nil :: 1 -> IntList
Cons :: Int × IntList -> IntList
where 1is a unit set (set with one element) and A × Boperation is a cross product of two sets Aand B(that is, set of pairs (a, b)where agoes through all elements of Aand bgoes through all elements of B).
其中1是单位组(具有一个元件组)和A × B操作的两组矢量积A和B(即,对一组(a, b),其中a通过的所有元素进入A和b通过的所有元素去B)。
Disjoint union of two sets Aand Bis a set A | Bwhich is a union of sets {(a, 1) : a in A}and {(b, 2) : b in B}. Essentially it is a set of all elements from both Aand B, but with each of this elements 'marked' as belonging to either Aor B, so when we pick any element from A | Bwe will immediately know whether this element came from Aor from B.
两个集合的不相交并集AandB是一个集合A | B,它是集合{(a, 1) : a in A}和 的并集{(b, 2) : b in B}。本质上,它是来自A和的所有元素的集合B,但是每个元素都被“标记”为属于A或B,因此当我们从 中选择任何元素时,A | B我们将立即知道该元素是来自A还是来自B。
We can 'join' Niland Consfunctions, so they will form a single function working on a set 1 | (Int × IntList):
我们可以“连接”Nil和Cons函数,因此它们将形成一个处理集合的单个函数1 | (Int × IntList):
Nil|Cons :: 1 | (Int × IntList) -> IntList
Indeed, if Nil|Consfunction is applied to ()value (which, obviously, belongs to 1 | (Int × IntList)set), then it behaves as if it was Nil; if Nil|Consis applied to any value of type (Int, IntList)(such values are also in the set 1 | (Int × IntList), it behaves as Cons.
事实上,如果Nil|Cons函数应用于()value(显然,它属于1 | (Int × IntList)set),那么它的行为就好像它是Nil; ifNil|Cons应用于任何类型的(Int, IntList)值(此类值也在 set 中1 | (Int × IntList),它的行为与Cons.
Now consider another datatype:
现在考虑另一种数据类型:
data IntTree = Leaf Int | Branch (IntTree, IntTree)
It has the following constructors:
它有以下构造函数:
Leaf :: Int -> IntTree
Branch :: (IntTree, IntTree) -> IntTree
which also can be joined into one function:
也可以合并为一个函数:
Leaf|Branch :: Int | (IntTree × IntTree) -> IntTree
It can be seen that both of this joinedfunctions have similar type: they both look like
可以看出,这两个joined函数的类型相似:它们都看起来像
f :: F T -> T
where Fis a kind of transformation which takes our type and gives more complex type, which consists of xand |operations, usages of Tand possibly other types. For example, for IntListand IntTreeFlooks as follows:
这里F是一种转型这需要我们的类型,并给出更复杂的类型,其中包括x与|作业,用途T以及可能的其他类型。例如,forIntList和IntTreeF看起来如下:
F1 T = 1 | (Int × T)
F2 T = Int | (T × T)
We can immediately notice that any algebraic type can be written in this way. Indeed, that is why they are called 'algebraic': they consist of a number of 'sums' (unions) and 'products' (cross products) of other types.
我们可以立即注意到任何代数类型都可以这样写。事实上,这就是它们被称为“代数”的原因:它们由许多其他类型的“和”(联合)和“乘积”(交叉乘积)组成。
Now we can define F-algebra. F-algebrais just a pair (T, f), where Tis some type and fis a function of type f :: F T -> T. In our examples F-algebras are (IntList, Nil|Cons)and (IntTree, Leaf|Branch). Note, however, that despite that type of ffunction is the same for each F, Tand fthemselves can be arbitrary. For example, (String, g :: 1 | (Int x String) -> String)or (Double, h :: Int | (Double, Double) -> Double)for some gand hare also F-algebras for corresponding F.
现在我们可以定义 F 代数了。F-algebra只是一个 pair (T, f),其中T是 some type 并且f是type的函数f :: F T -> T。在我们的例子中,F-代数是(IntList, Nil|Cons)和(IntTree, Leaf|Branch)。但是请注意,尽管这种类型的f功能是按照每个F相同的,T并且f自己可以随心所欲。例如,(String, g :: 1 | (Int x String) -> String)或(Double, h :: Int | (Double, Double) -> Double)对于某些g和h也是相应 F 的 F 代数。
Afterwards we can introduce F-algebra homomorphismsand then initial F-algebras, which have very useful properties. In fact, (IntList, Nil|Cons)is an initial F1-algebra, and (IntTree, Leaf|Branch)is an initial F2-algebra. I will not present exact definitions of these terms and properties since they are more complex and abstract than needed.
之后我们可以引入F-代数同态,然后是初始 F-代数,它们具有非常有用的性质。事实上,(IntList, Nil|Cons)是一个初 F1-代数,(IntTree, Leaf|Branch)是一个初 F2-代数。我不会给出这些术语和属性的确切定义,因为它们比需要的更复杂和抽象。
Nonetheless, the fact that, say, (IntList, Nil|Cons)is F-algebra allows us to define fold-like function on this type. As you know, fold is a kind of operation which transforms some recursive datatype in one finite value. For example, we can fold a list of integer into a single value which is a sum of all elements in the list:
尽管如此,例如(IntList, Nil|Cons)F-代数这一事实允许我们fold在这种类型上定义-like 函数。如您所知,折叠是一种将某些递归数据类型转换为一个有限值的操作。例如,我们可以将一个整数列表折叠成一个值,该值是列表中所有元素的总和:
foldr (+) 0 [1, 2, 3, 4] -> 1 + 2 + 3 + 4 = 10
It is possible to generalize such operation on any recursive datatype.
可以在任何递归数据类型上推广此类操作。
The following is a signature of foldrfunction:
以下是foldr函数的签名:
foldr :: ((a -> b -> b), b) -> [a] -> b
Note that I have used braces to separate first two arguments from the last one. This is not real foldrfunction, but it is isomorphic to it (that is, you can easily get one from the other and vice versa). Partially applied foldrwill have the following signature:
请注意,我使用大括号将前两个参数与最后一个参数分开。这不是真正的foldr函数,但它是同构的(也就是说,您可以轻松地从另一个函数中获取一个函数,反之亦然)。部分应用foldr将具有以下签名:
foldr ((+), 0) :: [Int] -> Int
We can see that this is a function which takes a list of integers and returns a single integer. Let's define such function in terms of our IntListtype.
我们可以看到这是一个函数,它接受一个整数列表并返回一个整数。让我们根据我们的IntList类型定义这样的函数。
sumFold :: IntList -> Int
sumFold Nil = 0
sumFold (Cons x xs) = x + sumFold xs
We see that this function consists of two parts: first part defines this function's behavior on Nilpart of IntList, and second part defines function's behavior on Conspart.
我们看到这个函数由两部分组成:第一部分定义了这个函数在Nil部分上的行为IntList,第二部分定义了函数在Cons部分上的行为。
Now suppose that we are programming not in Haskell but in some language which allows usage of algebraic types directly in type signatures (well, technically Haskell allows usage of algebraic types via tuples and Either a bdatatype, but this will lead to unnecessary verbosity). Consider a function:
现在假设我们不是用 Haskell 编程,而是用某种允许在类型签名中直接使用代数类型的语言(好吧,从技术上讲,Haskell 允许通过元组和Either a b数据类型使用代数类型,但这会导致不必要的冗长)。考虑一个函数:
reductor :: () | (Int × Int) -> Int
reductor () = 0
reductor (x, s) = x + s
It can be seen that reductoris a function of type F1 Int -> Int, just as in definition of F-algebra! Indeed, the pair (Int, reductor)is an F1-algebra.
可以看出它reductor是一个类型的函数F1 Int -> Int,就像在F-代数的定义中一样!事实上,这对(Int, reductor)是一个 F1 代数。
Because IntListis an initial F1-algebra, for each type Tand for each function r :: F1 T -> Tthere exist a function, called catamorphismfor r, which converts IntListto T, and such function is unique. Indeed, in our example a catamorphism for reductoris sumFold. Note how reductorand sumFoldare similar: they have almost the same structure! In reductordefinition sparameter usage (type of which corresponds to T) corresponds to usage of the result of computation of sumFold xsin sumFolddefinition.
因为IntList是一个初始 F1-代数,所以对于每种类型T和每个函数都r :: F1 T -> T存在一个函数,称为catamorphismfor r,它转换IntList为T,并且这样的函数是唯一的。实际上,在我们的示例中, 的 catamorphismreductor是sumFold。请注意如何reductor和sumFold相似:它们具有几乎相同的结构!在reductor定义s参数用法(其类型对应于T)对应于计算结果的使用sumFold xs中sumFold的定义。
Just to make it more clear and help you see the pattern, here is another example, and we again begin from the resulting folding function. Consider appendfunction which appends its first argument to second one:
只是为了更清楚并帮助您查看模式,这里是另一个示例,我们再次从生成的折叠函数开始。考虑append将第一个参数附加到第二个参数的函数:
(append [4, 5, 6]) [1, 2, 3] = (foldr (:) [4, 5, 6]) [1, 2, 3] -> [1, 2, 3, 4, 5, 6]
This how it looks on our IntList:
这是它在我们的外观上的样子IntList:
appendFold :: IntList -> IntList -> IntList
appendFold ys () = ys
appendFold ys (Cons x xs) = x : appendFold ys xs
Again, let's try to write out the reductor:
再次,让我们尝试写出还原器:
appendReductor :: IntList -> () | (Int × IntList) -> IntList
appendReductor ys () = ys
appendReductor ys (x, rs) = x : rs
appendFoldis a catamorphism for appendReductorwhich transforms IntListinto IntList.
appendFold是appendReductor转化IntList为的 catamorphism IntList。
So, essentially, F-algebras allow us to define 'folds' on recursive datastructures, that is, operations which reduce our structures to some value.
因此,本质上,F 代数允许我们在递归数据结构上定义“折叠”,即将我们的结构减少到某个值的操作。
F-coalgebras
F-代数
F-coalgebras are so-called 'dual' term for F-algebras. They allow us to define unfoldsfor recursive datatypes, that is, a way to construct recursive structures from some value.
F-coalgebras 是 F-代数的所谓“对偶”项。它们允许我们定义unfolds递归数据类型,即一种从某个值构造递归结构的方法。
Suppose you have the following type:
假设您有以下类型:
data IntStream = Cons (Int, IntStream)
This is an infinite stream of integers. Its only constructor has the following type:
这是一个无限的整数流。它唯一的构造函数具有以下类型:
Cons :: (Int, IntStream) -> IntStream
Or, in terms of sets
或者,就集合而言
Cons :: Int × IntStream -> IntStream
Haskell allows you to pattern match on data constructors, so you can define the following functions working on IntStreams:
Haskell 允许您对数据构造函数进行模式匹配,因此您可以定义以下用于IntStreams 的函数:
head :: IntStream -> Int
head (Cons (x, xs)) = x
tail :: IntStream -> IntStream
tail (Cons (x, xs)) = xs
You can naturally 'join' these functions into single function of type IntStream -> Int × IntStream:
您可以自然地将这些函数“加入”到类型为的单个函数中IntStream -> Int × IntStream:
head&tail :: IntStream -> Int × IntStream
head&tail (Cons (x, xs)) = (x, xs)
Notice how the result of the function coincides with algebraic representation of our IntStreamtype. Similar thing can also be done for other recursive data types. Maybe you already have noticed the pattern. I'm referring to a family of functions of type
注意函数的结果如何与我们IntStream类型的代数表示一致。对其他递归数据类型也可以做类似的事情。也许你已经注意到了这种模式。我指的是一系列类型的函数
g :: T -> F T
where Tis some type. From now on we will define
哪里T有某种类型。从现在开始我们将定义
F1 T = Int × T
Now, F-coalgebrais a pair (T, g), where Tis a type and gis a function of type g :: T -> F T. For example, (IntStream, head&tail)is an F1-coalgebra. Again, just as in F-algebras, gand Tcan be arbitrary, for example,(String, h :: String -> Int x String)is also an F1-coalgebra for some h.
现在,F-coalgebra是一对(T, g),其中T是一个类型,是一个类型g的函数g :: T -> F T。例如,(IntStream, head&tail)是 F1-coalgebra。同样,就像在 F-代数中一样,g并且T可以是任意的,例如,(String, h :: String -> Int x String)对于某些 h 也是 F1-coalgebra。
Among all F-coalgebras there are so-called terminal F-coalgebras, which are dual to initial F-algebras. For example, IntStreamis a terminal F-coalgebra. This means that for every type Tand for every function p :: T -> F1 Tthere exist a function, called anamorphism, which converts Tto IntStream, and such function is unique.
在所有 F-coalgebras 中有所谓的终端 F-coalgebras,它是对初始 F-代数的对偶。例如,IntStream是一个终端 F-coalgebra。这意味着对于每个类型T和每个函数都p :: T -> F1 T存在一个称为anamorphism的函数,它转换T为IntStream,并且这样的函数是唯一的。
Consider the following function, which generates a stream of successive integers starting from the given one:
考虑以下函数,该函数从给定的整数开始生成连续整数流:
nats :: Int -> IntStream
nats n = Cons (n, nats (n+1))
Now let's inspect a function natsBuilder :: Int -> F1 Int, that is, natsBuilder :: Int -> Int × Int:
现在让我们检查一个函数natsBuilder :: Int -> F1 Int,即natsBuilder :: Int -> Int × Int:
natsBuilder :: Int -> Int × Int
natsBuilder n = (n, n+1)
Again, we can see some similarity between natsand natsBuilder. It is very similar to the connection we have observed with reductors and folds earlier. natsis an anamorphism for natsBuilder.
同样,我们可以看到nats和之间的一些相似之处natsBuilder。这与我们之前观察到的减速器和折叠的连接非常相似。nats是 的变形natsBuilder。
Another example, a function which takes a value and a function and returns a stream of successive applications of the function to the value:
另一个例子,一个函数接受一个值和一个函数,并返回该函数对值的连续应用程序流:
iterate :: (Int -> Int) -> Int -> IntStream
iterate f n = Cons (n, iterate f (f n))
Its builder function is the following one:
它的构建器功能如下:
iterateBuilder :: (Int -> Int) -> Int -> Int × Int
iterateBuilder f n = (n, f n)
Then iterateis an anamorphism for iterateBuilder.
然后iterate是 的变形iterateBuilder。
Conclusion
结论
So, in short, F-algebras allow to define folds, that is, operations which reduce recursive structure down into a single value, and F-coalgebras allow to do the opposite: construct a [potentially] infinite structure from a single value.
因此,简而言之,F-代数允许定义折叠,即将递归结构缩减为单个值的操作,而 F-coalgebras 允许做相反的事情:从单个值构造 [潜在] 无限结构。
In fact in Haskell F-algebras and F-coalgebras coincide. This is a very nice property which is a consequence of presence of 'bottom' value in each type. So in Haskell both folds and unfolds can be created for every recursive type. However, theoretical model behind this is more complex than the one I have presented above, so I deliberately have avoided it.
事实上,在 Haskell 中,F-代数和 F-coalgebras 是重合的。这是一个非常好的属性,它是每种类型中存在“底部”值的结果。所以在 Haskell 中,可以为每个递归类型创建折叠和展开。然而,这背后的理论模型比我上面介绍的更复杂,所以我特意避开了它。
Hope this helps.
希望这可以帮助。
回答by zurgl
Going through the tutorial paper A tutorial on (co)algebras and (co)inductionshould give you some insight about co-algebra in computer science.
阅读教程论文A tutorial on (co)algebras and (co)induction应该会让您对计算机科学中的协代数有所了解。
Below is a citation of it to convince you,
下面引用它来说服你,
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.
一般而言,某种编程语言中的程序会操作数据。在过去几十年计算机科学的发展过程中,对这些数据的抽象描述是可取的,例如,以确保一个程序不依赖于它运行的数据的特定表示,这一点变得很明显。此外,这种抽象性有助于正确性证明。
这种愿望导致在计算机科学中使用代数方法,在一个称为代数规范或抽象数据类型理论的分支中。研究对象是数据类型本身,使用代数中熟悉的技术概念。计算机科学家使用的数据类型通常是从给定的(构造函数)操作集合中生成的,正因为如此,代数的“初始性”才起着如此重要的作用。
标准代数技术已被证明可用于捕获计算机科学中使用的数据结构的各种基本方面。但结果证明很难用代数描述计算中出现的一些固有的动态结构。这种结构通常涉及状态的概念,它可以通过各种方式进行转换。这种基于状态的动态系统的正式方法通常使用自动机或转换系统,作为经典的早期参考。
在过去的十年中,这种基于状态的系统不应该被描述为代数,而应该被描述为所谓的共同代数。这些是代数的形式对偶,在本教程中将详细说明。代数的“初始性”的双重性质,即终结性,对这种协代数来说是至关重要的。而这种最终共代数所需要的逻辑推理原理不是归纳而是共同归纳。
Prelude, about Category theory.Category theory should be rename theory of functors. As categories are what one must define in order to define functors. (Moreover, functors are what one must define in order to define natural transformations.)
序言,关于范畴论。范畴论应该是重命名函子理论。因为类别是为了定义函子必须定义的东西。(此外,为了定义自然变换,必须定义函子。)
What's a functor?It's a transformation from one set to another which preserving their structure. (For more detail there is a lot of good description on the net).
什么是函子?这是从一组到另一组的转换,保留了它们的结构。(有关更多详细信息,网上有很多很好的描述)。
What's is an F-algebra?It's the algebra of functor. It's just the study of the universal propriety of functor.
什么是 F 代数?这是函子的代数。这只是对函子的普遍性质的研究。
How can it be link to computer science ?Program can be view as a structured set of information. Program's execution correspond to modification of this structured set of information. It sound good that execution should preserve the program structure. Then execution can be view as the application of a functor over this set of information. (The one defining the program).
它如何与计算机科学联系起来?程序可以被视为一组结构化的信息。程序的执行对应于该结构化信息集的修改。执行应该保留程序结构听起来不错。那么执行可以被看作是一个函子在这组信息上的应用。(定义程序的那个)。
Why F-co-algebra ?Program are dual by essence as they are describe by information and they act on it. Then mainly the information which compose program and make them changed can be view in two way.
为什么是 F-协代数?程序本质上是双重的,因为它们是由信息描述的,并且它们会根据信息进行操作。那么主要是可以通过两种方式查看构成程序和改变程序的信息。
- Data which can be define as the information being processed by the program.
- State which can be define as the information being shared by the program.
- 可以定义为程序正在处理的信息的数据。
- 状态可以定义为程序共享的信息。
Then at this stage, I'd like to say that,
那么在这个阶段,我想说的是,
- F-algebra is the study of functorial transformation acting over Data's Universe (as been defined here).
- F-co-algebras is the study of functorial transformation acting on State's Universe (as been defined here).
- F-代数是对作用于数据宇宙(如此处定义)的函式变换的研究。
- F-co-algebras 是对作用于 State's Universe(如此处定义)的函式变换的研究。
During the life of a program, data and state co-exist, and they complete each other. They are dual.
在程序的生命周期中,数据和状态共存,并且相互完善。他们是双重的。
回答by isomorphismes
I'll start with stuff that is obviously programming-related and then add on some mathematics stuff, to keep it as concrete and down-to-earth as I can.
我将从明显与编程相关的内容开始,然后添加一些数学内容,以使其尽可能具体和实际。
Let's quote some computer-scientists on coinduction…
让我们引用一些关于联合归纳的计算机科学家的话......
http://www.cs.umd.edu/~micinski/posts/2012-09-04-on-understanding-coinduction.html
http://www.cs.umd.edu/~micinski/posts/2012-09-04-on-understanding-coinduction.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:
归纳法是关于有限数据,共同归纳法是关于无限数据。
无限数据的典型例子是惰性列表(流)的类型。例如,假设我们在内存中有以下对象:
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:
计算机不能容纳所有的 π,因为它只有有限的内存量!但它可以做的是持有一个有限程序,它会产生任何你想要的任意长的 π 展开。只要您只使用列表的有限部分,您就可以根据需要使用该无限列表进行计算。
但是,请考虑以下程序:
let print_third_element (k : int list) = match k with
| _ :: _ :: thd :: tl -> print thd
print_third_element pi
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.
这个程序应该打印 pi 的第三位数字。但是在某些语言中,函数的任何参数在传递给函数之前都会被评估(严格的,而不是惰性的,评估)。如果我们使用这个归约顺序,那么我们上面的程序将永远运行计算 pi 的数字,然后才能将其传递给我们的打印机函数(这永远不会发生)。由于机器没有无限内存,程序最终会耗尽内存并崩溃。这可能不是最好的评估顺序。
http://adam.chlipala.net/cpdt/html/Coinductive.html
http://adam.chlipala.net/cpdt/html/Coinductive.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.
在像 Haskell 这样的惰性函数式编程语言中,无限数据结构无处不在。无限列表和更奇特的数据类型为程序各部分之间的通信提供了方便的抽象。在许多情况下,在没有无限惰性结构的情况下实现类似的便利需要控制流的特技反转。
http://www.alexandrasilva.org/#/talks.html
http://www.alexandrasilva.org/#/talks.html
Relating the ambient mathematical context to usual programming tasks
将环境数学上下文与通常的编程任务相关联
What is "an algebra"?
什么是“代数”?
Algebraic structures generally look like:
代数结构通常如下所示:
- Stuff
- What the stuff can do
- 东西
- 东西能做什么
This should sound like objects with 1. properties and 2. methods. Or even better, it should sound like type signatures.
这听起来应该像具有 1. 属性和 2. 方法的对象。或者甚至更好,它应该听起来像类型签名。
Standard mathematical examples include monoid ? group ? vector-space ? "an algebra". Monoids are like automata: sequences of verbs (eg, f.g.h.h.nothing.f.g.f). A gitlog that always adds history and never deletes it would be a monoid but not a group. If you add inverses (eg negative numbers, fractions, roots, deleting accumulated history, un-shattering a broken mirror) you get a group.
标准数学示例包括幺半群?团体 ?向量空间 ? “一个代数”。Monoids 就像自动机:动词序列(例如,f.g.h.h.nothing.f.g.f)。一个git总是添加历史记录并且从不删除它的日志将是一个幺半群,而不是一个组。如果您添加逆数(例如负数、分数、根、删除累积的历史、取消破碎的镜像),您将得到一个组。
Groups contain things that can be added or subtracted together. For example Durations can be added together. (But Dates cannot.) Durations live in a vector-space (not just a group) because they can also be scaled by outside numbers. (A type signature of scaling :: (Number,Duration) → Duration.)
组包含可以一起添加或减去的内容。例如Durations 可以加在一起。(但Dates 不能。)持续时间存在于向量空间(不仅仅是一个组)中,因为它们也可以通过外部数字进行缩放。( . 的类型签名scaling :: (Number,Duration) → Duration)
Algebras ? vector-spaces can do yet another thing: there's some m :: (T,T) → T. Call this "multiplication" or don't, because once you leave Integersit's less obvious what "multiplication" (or "exponentiation") should be.
代数?向量空间还可以做另一件事:有一些m :: (T,T) → T. 或不Integers称其为“乘法”,因为一旦离开,“乘法”(或“求幂”)应该是什么就不那么明显了。
(This is why people look to (category-theoretic) universal properties: to tell them what multiplication should door be like:
(这就是为什么人们期待(范畴论)普遍属性:告诉他们乘法应该做什么或像什么:
)
)
Algebras → Coalgebras
代数 → 代数
Comultiplication is easier to define in a way that feels non-arbitrary, than is multiplication, because to go from T → (T,T)you can just repeat the same element. ("diagonal map" – like diagonal matrices/operators in spectral theory)
协乘比乘法更容易以一种感觉非任意的方式定义,因为从T → (T,T)你开始,你可以重复相同的元素。(“对角线图”——就像谱理论中的对角线矩阵/运算符)
Counit is usually the trace (sum of diagonal entries), although again what's important is what your counit does; traceis just a good answer for matrices.
Counit 通常是跟踪(对角线条目的总和),尽管同样重要的是您的 counit做了什么;trace只是矩阵的一个很好的答案。
The reason to look at a dual space, in general, is because it's easier to think in that space. For example it's sometimes easier to think about a normal vector than about the plane it's normal to, but you can control planes (including hyperplanes) with vectors (and now I'm speaking of the familiar geometric vector, like in a ray-tracer).
一般而言,查看双空间的原因是因为在该空间中更容易思考。例如,有时考虑法向量比考虑它法线的平面更容易,但是您可以使用向量控制平面(包括超平面)(现在我说的是熟悉的几何向量,就像在光线跟踪器中一样) .
Taming (un)structured data
驯服(非)结构化数据
Mathematicians might be modelling something fun like TQFT's, whereas programmers have to wrestle with
数学家可能正在建模一些有趣的东西,比如TQFT,而程序员必须与
- dates/times (
+ :: (Date,Duration) → Date), - places (
Paris≠(+48.8567,+2.3508)! It's a shape, not a point.), - unstructured JSON which is supposed to be consistent in some sense,
- wrong-but-close XML,
- incredibly complex GIS data which should satisfy loads of sensible relations,
- regular expressions which meantsomething to you, but mean considerably less to perl.
- CRM that should hold all the executive's phone numbers and villa locations, his (now ex-) wife and kids' names, birthday and all the previous gifts, each of which should satisfy "obvious" relations (obvious to the customer) which are incredibly hard to code up,
- .....
- 日期/时间 (
+ :: (Date,Duration) → Date), - 地方(
Paris≠(+48.8567,+2.3508)!这是一个形状,而不是一个点。), - 在某种意义上应该是一致的非结构化 JSON,
- 错误但关闭的 XML,
- 非常复杂的 GIS 数据,应该满足大量合理的关系,
- 正则表达式对你来说意味着什么,但对 perl 的意义要小得多。
- CRM 应该保存所有高管的电话号码和别墅位置、他(现在的前任)妻子和孩子的名字、生日和所有以前的礼物,每一个都应该满足“明显”的关系(对客户来说是显而易见的),这是令人难以置信的很难编码,
- .....
Computer scientists, when talking about coalgebras, usually have set-ish operations in mind, like Cartesian product. I believe this is what people mean when they say like "Algebras are coalgebras in Haskell". But to the extent that programmers have to model complex data-types like Place, Date/Time, and Customer—and make those models look as much like the real world (or at least the end-user's view of the real world) as possible—I believe duals, could be useful beyond only set-world.
计算机科学家在谈论余代数时,通常会想到集合运算,例如笛卡尔积。我相信这就是人们说“代数是 Haskell 中的代数”时的意思。但在某种程度上,程序员必须对像Place、Date/Time和等复杂数据类型Customer进行建模——并使这些模型看起来尽可能像现实世界(或者至少是最终用户对现实世界的看法)——我相信对偶,可能不仅适用于场景世界。

