简体   繁体   English

“编程代码”在编程环境中意味着什么?

[英]What does “coalgebra” mean in the context of programming?

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. 我在函数式编程和PLT圈子中多次听到过“enggebras”这个术语,特别是在讨论对象,comonads,镜头等时。 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? 任何人都可以解释一下代数在编程环境中的意义,它们的意义是什么,以及它们与对象和共同体的关系?

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. 大多数时候,这些东西是以集合的形式引入的,但由于我们是朋友之间,我将讨论Haskell类型。 (I can't resist using some Greek letters though—they make everything look cooler!) (我无法抗拒使用一些希腊字母 - 它们让一切看起来都更酷!)

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 (τ, τ,…, τ) → τ . 这些函数采用不同数量的τ类型的参数并产生τ :uncurried,它们看起来都像(τ, τ,…, τ) → τ 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 ∷ τ . 幺半群是任何类型τ ,具有函数mappend ∷ (τ, τ) → τ和同一性mzero ∷ τ Other examples include things like groups (which are just like monoids except with an extra invert ∷ τ → τ function), rings, lattices and so on. 其他例子包括诸如组之类的东西(除了具有额外的invert ∷ τ → τ函数之外,它们就像幺半群一样),环,格子等。

All the functions operate on τ but can have different arities. 所有功能都在τ运行,但可以有不同的特性。 We can write these out as τⁿ → τ , where τⁿ maps to a tuple of n τ . 我们可以写这些出来作为τⁿ → τ ,其中τⁿ映射到的元组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. 所以我们现在实际上可以简化代数的概念:它只是一些带有一些函数的类型。

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 ∷ () → τ . 特别是,让我们看看幺半群:我们有mappend ∷ (τ, τ) → τmempty ∷ () → τ We can turn these into a single function using a sum type— Either . 我们可以使用类型A和把这些成一个单一的功能, Either It would look like this: 它看起来像这样:

op ∷ Monoid τ ⇒ Either (τ, τ) () → τ
op (Left (a, b)) = mappend (a, b)
op (Right ())    = mempty

We can actually use this transformation repeatedly to combine all the τⁿ → τ functions into a single one, for any algebra. 对于任何代数,我们实际上可以反复使用这个变换将所有 τⁿ → τ函数组合成一个函数。 (In fact, we can do this for any number of functions a → τ , b → τ and so on for any a, b,… .) (事实上​​,对于任何 a, b,… ,我们可以为任意数量的函数a → τb → τ等执行此操作。)

This lets us talk about algebras as a type τ with a single function from some mess of Either s to a single τ . 这让我们谈论代数作为一种τ从一些乱七八糟的单一功能Either s到一个单一的τ For monoids, this mess is: Either (τ, τ) () ; 对于幺半群,这个混乱是: Either (τ, τ) () ; for groups (which have an extra τ → τ operation), it's: Either (Either (τ, τ) τ) () . 对于组(具有额外的τ → τ操作),它是: 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 any monoid τ: 例如,对于monoids,我们可以创建一个适用于任何 monoidτ的monoid参数类型:

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 ! 好吧,他们都是Functors Eg: 例如:

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 . 对于某些函子f它只是某种类型τ ,函数f τ → τ In fact, we could write this out as a typeclass: 事实上,我们可以把它写成一个类型类:

class Functor f ⇒ Algebra f τ where
  op ∷ f τ → τ

This is often called an "F-algebra" because it's determined by the functor F . 这通常被称为“F代数”,因为它由函子F确定。 If we could partially apply typeclasses, we could define something like class Monoid = Algebra MonoidArgument . 如果我们可以部分应用类型类,我们可以定义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? 什么是F-coalgebra? Well, the co implies that it's the "dual" of an algebra—that is, we take an algebra and flip some arrows. 好吧,co意味着它是代数的“双重” - 也就是说,我们采用代数并翻转一些箭头。 I only see one arrow in the above definition, so I'll just flip that: 我只在上面的定义中看到一个箭头,所以我只是翻转它:

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 what a 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 C that contains all the possible internal states of objects in the class; 我们有一个C ,它包含了类中对象的所有可能的内部状态; the class itself is a coalgebra over C which specifies the methods and properties of the objects. 类本身是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. 如代数示例所示,如果我们有一堆函数,如a → τb → τ对于任何a, b,… ,我们可以使用Either (和类型)将它们全部组合成单个函数。 The dual "notion" would be combining a bunch of functions of type τ → a , τ → b and so on. 双重“概念”将组合τ → aτ → b等类型的一系列函数。 We can do this using the dual of a sum type—a product type. 我们可以使用sum类型的对偶 - 产品类型来实现这一点。 So given the two functions above (called f and g ), we can create a single one like so: 所以考虑到上面的两个函数(称为fg ),我们可以像这样创建一个函数:

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. 类型(a, a)是一种直接方式的仿函数,所以它肯定符合我们的F-余代数的概念。 This particular trick lets us package up a bunch of different functions—or, for OOP, methods—into a single function of type τ → f τ . 这个特殊的技巧让我们将一堆不同的函数 - 或者对于OOP,方法 - 打包成一个类型为τ → f τ函数。

The elements of our type C represent the internal state of the object. 类型C的元素表示对象的内部状态。 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 . 最明显的方法是使它们成为C的函数。 So if we want a length property (eg object.length ), we would have a function C → Int . 因此,如果我们想要一个长度属性(例如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 . 为此,我们需要获取所有参数并生成一个新的C Let's imagine a setPosition method which takes an x and a y coordinate: object.setPosition(1, 2) . 让我们设想一个采用xy坐标的setPosition方法: object.setPosition(1, 2) It would look like this: C → ((Int, Int) → C) . 它看起来像这样: 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 self parameter in Python and like the implicit this of many other languages. 这就像self在Python参数和喜欢的隐性this许多其他语言的。 A coalgebra essentially just encapsulates the behavior of taking a self parameter: that's what the first C in C → FC is. 一个代数基本上只是封装了一个self参数的行为:这就是C → FC的第一个C

So let's put it all together. 所以我们把它们放在一起吧。 Let's imagine a class with a position property, a name property and setPosition function: 让我们设想一个具有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 Int s and a String . 在这种情况下,它只包含两个Int和一个String (This is our type C .) Then we need to come up with the coalgebra representing the class. (这是我们的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 self variables. 这就像一个带有显式self变量的Python类。 Now that we have a bunch of self → functions, we need to combine them into a single function for the coalgebra. 现在我们有了一堆self →函数,我们需要将它们组合成一个用于代数的单个函数。 We can do this with a simple tuple: 我们可以用一个简单的元组来做到这一点:

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 any c —is a functor, so coop does have the form we want: Functor f ⇒ C → f C . 类型((Int, Int), String, (Int, Int) → c) - 对于任何 c - 是一个仿函数,所以coop确实具有我们想要的形式: Functor f ⇒ C → f C

Given this, C along with coop form a coalgebra which specifies the class I gave above. 鉴于此, Ccoop构成了一个代数,它指定了我上面给出的类。 You can see how we can use this same technique to specify any number of methods and properties for our objects to have. 您可以看到我们如何使用相同的技术为对象指定任意数量的方法和属性。

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. 例如,我们可以引入“F-coalgebra同态”的概念来表示类之间的转换。 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. 这使得考虑将类映射到其他类更容易。

In short, an F-coalgebra represents a class by having a bunch of properties and methods that all depend on a self parameter containing each object's internal state. 简而言之,F-coalgebra通过拥有一堆属性和方法来表示一个类,这些属性和方法都依赖于包含每个对象内部状态的self参数。

Other Categories 其他类别

So far, we've talked about algebras and coalgebras as Haskell types. 到目前为止,我们已经将代数和余代数作为Haskell类型进行了讨论。 An algebra is just a type τ with a function f τ → τ and a coalgebra is just a type τ with a function τ → f τ . 一个代数仅仅是一个类型τ与函数f τ → τ和一个余代数只是一个类型τ与函数τ → f τ

However, nothing really ties these ideas to Haskell per se . 然而,没有什么能真正将这些想法与Haskell 本身联系起来 In fact, they're usually introduced in terms of sets and mathematical functions rather than types and Haskell functions. 事实上,它们通常是根据集合和数学函数而不是类型和Haskell函数引入的。 Indeed,we can generalize these concepts to any categories! 实际上,我们可以将这些概念概括为任何类别!

We can define an F-algebra for some category C . 我们可以为某些类别C定义F代数。 First, we need a functor F : C → C —that is, an endofunctor . 首先,我们需要一个仿函数F : C → C - 即一个endofunctor (All Haskell Functor s are actually endofunctors from Hask → Hask .) Then, an algebra is just an object A from C with a morphism FA → A . (所有Haskell Functor实际上都是来自Hask → Hask 。)然后,代数只是来自C的对象A ,具有态射FA → A A coalgebra is the same except with A → FA . 除了A → FA之外,余代数是相同的。

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 map function is just a proof of the fact that M is a Functor . map函数只是MFunctor这一事实的证明。 So we can say that a monad is just a functor with two operations: return and join . 所以我们可以说monad只是一个有两个操作的仿函数: returnjoin

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's a nice article helping explain the idea. 这是一篇很好的文章,有助于解释这个想法。 It talks about concat , which is just join for lists. 它谈论concat ,这是刚刚join的名单。

With Haskell functors, the composition of two functors is a functor itself. 使用Haskell仿函数,两个仿函数的组合本身就是仿函数。 In pseudocode, we could write this: 在伪代码中,我们可以这样写:

instance (Functor f, Functor g) ⇒ Functor (f ∘ g) where
  fmap fun x = fmap (fmap fun) x

This helps us think about join as a mapping from f ∘ f → f . 这有助于我们将join视为f ∘ f → f的映射。 The type of join is ∀α. f (f α) → f α 该类型的join∀α. f (f α) → f α ∀α. f (f α) → f α . ∀α. f (f α) → f α Intuitively, we can see how a function valid for all types α can be thought of as a transformation of f . 直观地,我们可以看到对所有类型α有效的函数如何被认为是f的变换。

return is a similar transformation. return是一种类似的转变。 Its type is ∀α. α → f α 它的类型是∀α. α → f α ∀α. α → f α . ∀α. α → f α This looks different—the first α is not "in" a functor! 这看起来不同 - 第一个α不是“in”的仿函数! Happily, we can fix this by adding an identity functor there: ∀α. Identity α → f α 令人高兴的是,我们可以通过在那里添加一个身份∀α. Identity α → f α函数来解决这个问题: ∀α. Identity α → f α ∀α. Identity α → f α . ∀α. Identity α → f α So return is a transformation Identity → f . 所以return是转换Identity → f

Now we can think about a monad as just an algebra based around some functor f with operations f ∘ f → f and Identity → f . 现在我们可以将monad想象成一个基于f ∘ f → ff的代数,其运算f ∘ f → fIdentity → f Doesn't this look familiar? 这看起来不熟悉吗? It's very similar to a monoid, which was just some type τ with operations τ × τ → τ and () → τ . 它与幺半群非常相似,它只是某种类型τ ,其操作τ × τ → τ() → τ

So a monad is just like a monoid, except instead of having a type we have a functor. 所以monad就像一个monoid,除了没有类型我们有一个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只是endofunctors类别中的monoid”这句话。)

Now, we have these two operations: f ∘ f → f and Identity → f . 现在,我们有这两个操作: f ∘ f → fIdentity → f To get the corresponding coalgebra, we just flip the arrows. 为了获得相应的代数,我们只需翻转箭头。 This gives us two new operations: f → f ∘ f and f → Identity . 这给了我们两个新的操作: f → f ∘ ff → Identity We can turn them into Haskell types by adding type variables as above, giving us ∀α. f α → f (f α) 我们可以通过添加上面的类型变量将它们变成Haskell类型,给我们∀α. f α → f (f α) ∀α. f α → f (f α) and ∀α. f α → α ∀α. f α → α ∀α. f α → f (f α) ∀α. f α → α ∀α. f α → f (f α)∀α. f α → α ∀α. f α → α . ∀α. f α → α This looks just like the definition of a comonad: 这看起来就像comonad的定义:

class Functor f ⇒ Comonad f where
  coreturn ∷ f α → α
  cojoin   ∷ f α → f (f α)

So a comonad is then a coalgebra in a category of endofunctors. 所以comonad然后在endofunctors的类别余代数

F-algebras and F-coalgebras are mathematical structures which are instrumental in reasoning about inductive types (or recursive types ). F-代数和F-余代数是数学结构,有助于推理归纳类型 (或递归类型 )。

F-algebras F-代数

We'll start first with F-algebras. 我们先从F-algebras开始。 I will try to be as simple as possible. 我会尽量简单。

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 Nil as () -> IntList , not simply IntList . 请注意,我已将Nil类型写为() -> IntList ,而不仅仅是IntList These are in fact equivalent types from the theoretical point of view, because () type has only one inhabitant. 从理论的角度来看,这些实际上是等同的类型,因为()类型只有一个居民。

If we write signatures of these functions in a more set-theoretical way, we will get 如果我们以更集理论的方式编写这些函数的签名,我们就会得到

Nil  :: 1 -> IntList
Cons :: Int × IntList -> IntList

where 1 is a unit set (set with one element) and A × B operation is a cross product of two sets A and B (that is, set of pairs (a, b) where a goes through all elements of A and b goes through all elements of B ). 其中1是单位集(用一个元素设置), A × B操作是两个集合AB的交叉乘积(即成对的集合(a, b) ,其中a经过Ab所有元素去通过B )的所有元素。

Disjoint union of two sets A and B is a set A | B 两组AB不相交联合是一组A | B A | B which is a union of sets {(a, 1) : a in A} and {(b, 2) : b in B} . A | B是集合{(a, 1) : a in A}的集合{(a, 1) : a in A} {(b, 2) : b in B} Essentially it is a set of all elements from both A and B , but with each of this elements 'marked' as belonging to either A or B , so when we pick any element from A | B 本质上它是来自AB的所有元素的集合,但是每个元素'标记'属于AB ,所以当我们从A | B选择任何元素时 A | B we will immediately know whether this element came from A or from B . A | B我们将立即知道这个元素是来自A还是来自B

We can 'join' Nil and Cons functions, so they will form a single function working on a set 1 | (Int × IntList) 我们可以“加入” NilCons函数,因此它们将构成一个函数,用于集合1 | (Int × IntList) 1 | (Int × IntList) : 1 | (Int × IntList)

Nil|Cons :: 1 | (Int × IntList) -> IntList

Indeed, if Nil|Cons function is applied to () value (which, obviously, belongs to 1 | (Int × IntList) set), then it behaves as if it was Nil ; 实际上,如果将Nil|Cons函数应用于()值(显然属于1 | (Int × IntList)集),那么它的行为就好像它是Nil ; if Nil|Cons is applied to any value of type (Int, IntList) (such values are also in the set 1 | (Int × IntList) , it behaves as Cons . 如果Nil|Cons应用于任何类型的值(Int, IntList) (这些值也在集合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 joined functions have similar type: they both look like 可以看出,这两个joined函数都有类似的类型:它们看起来都像

f :: F T -> T

where F is a kind of transformation which takes our type and gives more complex type, which consists of x and | 其中F是一种变换,它采用我们的类型并给出更复杂的类型,它由x| operations, usages of T and possibly other types. 操作, T用法以及可能的其他类型。 For example, for IntList and IntTree F looks as follows: 例如,对于IntListIntTree F ,如下所示:

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代数。 F-algebra is just a pair (T, f) , where T is some type and f is a function of type f :: FT -> T . F代数只是一对(T, f) ,其中T是某种类型, ff :: FT -> T类型的函数。 In our examples F-algebras are (IntList, Nil|Cons) and (IntTree, Leaf|Branch) . 在我们的例子中,F-代数是(IntList, Nil|Cons)(IntTree, Leaf|Branch) Note, however, that despite that type of f function is the same for each F, T and f themselves can be arbitrary. 但是请注意,尽管每种F的f函数类型相同,但Tf本身可以是任意的。 For example, (String, g :: 1 | (Int x String) -> String) or (Double, h :: Int | (Double, Double) -> Double) for some g and h are also F-algebras for corresponding F. 例如,对于某些gh(String, g :: 1 | (Int x String) -> String)(Double, h :: Int | (Double, Double) -> Double)也是对应的F代数F。

Afterwards we can introduce F-algebra homomorphisms and then initial F-algebras , which have very useful properties. 之后我们可以引入F-代数同态 ,然后引入具有非常有用性质的初始F-代数 In fact, (IntList, Nil|Cons) is an initial F1-algebra, and (IntTree, Leaf|Branch) is an initial F2-algebra. 实际上, (IntList, Nil|Cons)是一个初始的F1代数,而(IntTree, Leaf|Branch)是一个初始的F2代数。 I will not present exact definitions of these terms and properties since they are more complex and abstract than needed. 我不会提供这些术语和属性的确切定义,因为它们比需要的更复杂和抽象。

Nonetheless, the fact that, say, (IntList, Nil|Cons) is F-algebra allows us to define fold -like function on this type. 但是,事实上, (IntList, Nil|Cons)是F代数允许我们在这种类型上定义类似fold的函数。 As you know, fold is a kind of operation which transforms some recursive datatype in one finite value. 如您所知,fold是一种将一些递归数据类型转换为一个有限值的操作。 For example, we can fold a list of integer into a single value which is a sum of all elements in the list: 例如,我们可以将整数列表折叠为单个值,该值是列表中所有元素的总和:

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 foldr function: 以下是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 foldr function, but it is isomorphic to it (that is, you can easily get one from the other and vice versa). 这不是真正的foldr函数,但它是同构的(也就是说,你可以很容易地从另一个中得到一个,反之亦然)。 Partially applied foldr will have the following signature: 部分应用的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 IntList type. 让我们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 Nil part of IntList , and second part defines function's behavior on Cons part. 我们看到这个函数由两部分组成:第一部分在IntList Nil部分定义了这个函数的行为,第二部分在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 ab datatype, but this will lead to unnecessary verbosity). 现在假设我们不是在Haskell中编程,而是在某种语言中允许直接在类型签名中使用代数类型(从技术上讲,Haskell允许通过元组和Either ab数据类型来使用代数类型,但这会导致不必要的冗长)。 Consider a function: 考虑一个功能:

reductor :: () | (Int × Int) -> Int
reductor ()     = 0
reductor (x, s) = x + s

It can be seen that reductor is a function of type F1 Int -> Int , just as in definition of F-algebra! 可以看出, reductorF1 Int -> Int类型的函数,就像F代数的定义一样! Indeed, the pair (Int, reductor) is an F1-algebra. 实际上,这对(Int, reductor)是一个F1代数。

Because IntList is an initial F1-algebra, for each type T and for each function r :: F1 T -> T there exist a function, called catamorphism for r , which converts IntList to T , and such function is unique. 因为IntList是初始F1-代数,对于每种类型T和每个功能r :: F1 T -> T存在的函数,称为catamorphismr它转换IntListT ,并且这样的功能是唯一的。 Indeed, in our example a catamorphism for reductor is sumFold . 实际上,在我们的例子中,缩减reductorsumFoldsumFold Note how reductor and sumFold are similar: they have almost the same structure! 注意reductorsumFold是如何相似的:它们具有几乎相同的结构! In reductor definition s parameter usage (type of which corresponds to T ) corresponds to usage of the result of computation of sumFold xs in sumFold definition. reductor定义s参数用法(其类型对应于T )对应于计算结果的使用sumFold xssumFold定义。

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 append function 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上的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

appendFold is a catamorphism for appendReductor which transforms IntList into IntList . appendFoldappendReductor一个catamorphism,它将IntList转换为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. F-coalgebras是F-algebras的所谓“双重”术语。 They allow us to define unfolds for recursive datatypes, that is, a way to construct recursive structures from some value. 它们允许我们为递归数据类型定义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 IntStream s: Haskell允许您对数据构造函数进行模式匹配,因此您可以定义在IntStreamIntStream的以下函数:

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类型的单个函数中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 IntStream type. 注意函数的结果如何与我们的IntStream类型的代数表示一致。 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 我指的是一系列类型的函数

g :: T -> F T

where T is some type. 其中T是某种类型。 From now on we will define 从现在开始,我们将定义

F1 T = Int × T

Now, F-coalgebra is a pair (T, g) , where T is a type and g is a function of type g :: T -> FT . 现在, F-coalgebra是一对(T, g) ,其中T是一个类型, gg :: T -> FT类型的函数。 For example, (IntStream, head&tail) is an F1-coalgebra. 例如, (IntStream, head&tail)是F1-coalgebra。 Again, just as in F-algebras, g and T can be arbitrary, for example, (String, h :: String -> Int x String) is also an F1-coalgebra for some h. 同样,就像在F-algebras中一样, gT可以是任意的,例如, (String, h :: String -> Int x String)也是某些h的F1-代数。

Among all F-coalgebras there are so-called terminal F-coalgebras , which are dual to initial F-algebras. 在所有的F-coalgebras中都有所谓的终端F-余代数 ,它们是初始F-代数的双重代数。 For example, IntStream is a terminal F-coalgebra. 例如, IntStream是终端F-代数。 This means that for every type T and for every function p :: T -> F1 T there exist a function, called anamorphism , which converts T to IntStream , and such function is unique. 这意味着对于每个类型T和每个函数p :: T -> F1 T ,存在一个称为变形的函数,它将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 nats and natsBuilder . 同样,我们可以看到的一些相似natsnatsBuilder It is very similar to the connection we have observed with reductors and folds earlier. 它与我们之前在减速器和折叠中观察到的连接非常相似。 nats is an anamorphism for natsBuilder . natsnatsBuilder的变形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 iterate is an anamorphism for iterateBuilder . 然后, iterateiterateBuilder一个变形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. 事实上在Haskell F-algebras和F-coalgebras重合。 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. 所以在Haskell中,可以为每个递归类型创建折叠和展开。 However, theoretical model behind this is more complex than the one I have presented above, so I deliberately have avoided it. 然而,这背后的理论模型比我上面提到的更复杂,所以我故意避免它。

Hope this helps. 希望这可以帮助。

Going through the tutorial paper A tutorial on (co)algebras and (co)induction should give you some insight about co-algebra in computer science. 阅读教程论文关于(共)代数和(共)归纳的教程应该会给你一些关于计算机科学中的共同代数的见解。

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? 什么是F代数? It's the algebra of functor. 它是仿函数的代数。 It's just the study of the universal propriety of functor. 这只是对仿函数普遍适用性的研究。

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 ? 为什么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. 那么主要是组成程序并使它们改变的信息可以以两种方式查看。

  • 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代数是对数据宇宙上的函数变换的研究(如这里定义的)。
  • F-co-algebras is the study of functorial transformation acting on State's Universe (as been defined here). F-co-algebras是研究状态宇宙上的函数变换(如这里定义的)。

During the life of a program, data and state co-exist, and they complete each other. 在程序的生命周期中,数据和状态共存,并且它们彼此完成。 They are dual. 他们是双重的。

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… 让我们引用一些关于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. 该程序应打印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). 如果我们使用这个减少顺序,那么我们上面的程序将永远运行计算pi的数字,然后才能传递给我们的打印机功能(从未发生)。 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.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. 在像Haskell这样的惰性函数式编程语言中,无处不在的数据结构。 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 http://www.alexandrasilva.org/#/talks.html 亚历山德拉席尔瓦的代数的例子


Relating the ambient mathematical context to usual programming tasks 将环境数学上下文与通常的编程任务相关联

What is "an algebra"? 什么是“代数”?

Algebraic structures generally look like: 代数结构通常如下所示:

  1. Stuff 东西
  2. What the stuff can do 这些东西可以做什么

This should sound like objects with 1. properties and 2. methods. 这应该听起来像1.属性和2.方法的对象。 Or even better, it should sound like type signatures. 或者甚至更好,它应该听起来像类型签名。

Standard mathematical examples include monoid ⊃ group ⊃ vector-space ⊃ "an algebra". 标准数学例子包括monoid⊃group⊃矢量空间⊃“代数”。 Monoids are like automata: sequences of verbs (eg, fghhnothing.fgf ). 幺半群就像自动机:动词序列(例如, fghhnothing.fgf )。 A git log that always adds history and never deletes it would be a monoid but not a group. 总是添加历史记录并且永远不会删除它的git日志将是一个monoid而不是一个组。 If you add inverses (eg negative numbers, fractions, roots, deleting accumulated history, un-shattering a broken mirror) you get a group. 如果你添加了反转(例如负数,分数,根,删除累积的历史记录,不破坏破碎的镜像),你会得到一个组。

Groups contain things that can be added or subtracted together. 组包含可以一起添加或减去的内容。 For example Duration s can be added together. 例如,可以将Duration s加在一起。 (But Date s cannot.) Durations live in a vector-space (not just a group) because they can also be scaled by outside numbers. (但是Date不能。)持续时间存在于向量空间(不仅仅是一个组),因为它们也可以通过外部数字进行缩放。 (A type signature of scaling :: (Number,Duration) → Duration .) scaling :: (Number,Duration) → Duration的类型签名scaling :: (Number,Duration) → Duration 。)

Algebras ⊂ vector-spaces can do yet another thing: there's some m :: (T,T) → T . 代数⊂向量空间可以做另一件事:有一些m :: (T,T) → T Call this "multiplication" or don't, because once you leave Integers it's less obvious what "multiplication" (or "exponentiation" ) should be. 称之为“乘法”或不这样做,因为一旦你离开Integers ,那么“乘法”(或“取幂” )应该是什么不太明显。

(This is why people look to (category-theoretic) universal properties: to tell them what multiplication should do or be like : (这就是人们关注(类别理论)通用属性的原因:告诉他们乘法应该做什么或者什么样的

产品的普遍属性 )


Algebras → Coalgebras 代数→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. 与乘法相比,乘法更容易以感觉非任意的方式定义,因为从T → (T,T)你可以重复相同的元素。 ("diagonal map" – like diagonal matrices/operators in spectral theory) (“对角线图” - 类似于光谱理论中的对角矩阵/算子)

Counit is usually the trace (sum of diagonal entries), although again what's important is what your counit does ; 计数通常是跟踪(对角线条目的总和),尽管重要的是你的国家所做的事情; trace is just a good answer for matrices. 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 ), 日期/时间( + :: (Date,Duration) → Date ),
  • places ( Paris(+48.8567,+2.3508) ! It's a shape, not a point.), 地方( Paris(+48.8567,+2.3508) !这是一个形状,而不是一个点。),
  • unstructured JSON which is supposed to be consistent in some sense, 非结构化的JSON,在某种意义上应该是一致的,
  • wrong-but-close XML, 错误但非常接近的XML,
  • incredibly complex GIS data which should satisfy loads of sensible relations, 非常复杂的GIS数据,应该满足合理的关系,
  • regular expressions which meant something to you, but mean considerably less to perl. 正则表达式对你来说意味着什么,但对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, 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. 但是程序员必须对像PlaceDate/TimeCustomer这样的复杂数据类型进行建模,并使这些模型看起来尽可能像现实世界(或者至少是最终用户对现实世界的看法) - 我相信双重身份,可能只有在世界范围内有用。

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

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