关于scala:“coalgebra”在编程环境中意味着什么?

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

我在函数编程和PLT循环中已经听过"coalgebras"这个词好几次了,特别是当讨论对象、辅音、透镜等等的时候。谷歌这个术语给出了一些页面,它们给出了这些结构的数学描述,我对此几乎无法理解。有人能解释一下coalgebras在编程环境中的含义,它们的意义是什么,以及它们如何与对象和Comonads相关吗?


代数

我认为,首先要了解代数的概念。这只是代数结构的一个推广,如群、环、单倍体等等。大多数时候,这些东西都是以集合的形式介绍的,但是由于我们是朋友,所以我将讨论haskell类型。(我忍不住用了一些希腊字母,尽管它们让一切看起来更酷!)好的。

那么,代数只是一个具有一些函数和恒等式的τ类型。这些函数接受不同数量的τ类型的参数并生成τ:未转换,它们看起来都像(τ, τ,…, τ) → τ。它们还可以具有"标识"(τ的元素),这些元素在某些功能上具有特殊的行为。好的。

最简单的例子是单倍体。单倍体是任何类型的τ,具有mappend ∷ (τ, τ) → τmzero ∷ τ的功能。其他的例子包括像群(除了具有额外的invert ∷ τ → τ函数外,这些群就像单体)、环、格等等。好的。

所有的函数都在τ上运行,但可以有不同的算术运算。我们可以把它们写成τ? → τ,其中τ?映射到nτ的一个元组。这样,我们就可以把身份看作是τ? → τ,其中τ?只是空元组()。所以我们现在可以简化代数的概念:它只是一种类型,上面有一些函数。好的。

代数只是数学中被"分解"的一种常见模式,就像我们处理代码一样。人们注意到前面提到的单体、群、格等一大堆有趣的东西都遵循类似的模式,所以他们把它抽象出来。这样做的好处与编程中的一样:它创建了可重用的证明,并使某些类型的推理更容易。好的。F-代数

然而,我们还没有完全完成因子分解。到目前为止,我们有一系列函数τ? → τ。我们实际上可以做一个巧妙的技巧,将它们组合成一个函数。特别是,让我们来看看monoids:我们有mappend ∷ (τ, τ) → τmempty ∷ () → τ。我们可以使用一个和类型-Either把这些转换成一个函数。如下所示:好的。

1
2
3
op ∷ Monoid τ ? Either (τ, τ) () → τ
op (Left (a, b)) = mappend (a, b)
op (Right ())    = mempty

对于任何代数,我们实际上都可以重复使用这个转换将所有的τ? → τ函数组合成一个函数。(事实上,我们可以为任意数量的函数a → τb → τ等为任何a, b,…执行此操作。)好的。

这让我们把代数看作是一个τ类型,它具有从一个Eithers的一个函数到一个τ的单一函数。对于幺半群,这种混乱是:Either (τ, τ) ();对于组(具有额外的EDCOX1×29操作),它是:Either (Either (τ, τ) τ) ()。对于不同的结构来说,这是一种不同的类型。那么,这些类型有什么共同点呢?最明显的是,它们都只是积和代数数据类型。例如,对于幺半群,我们可以创建一个幺半群参数类型,它适用于任何幺半群TA:好的。

1
2
data MonoidArgument τ = Mappend τ τ -- here τ τ is the same as (τ, τ)
                      | Mempty      -- here we can just leave the () out

我们可以对群、环、格以及所有其他可能的结构做同样的事情。好的。

所有这些类型还有什么特别之处?好吧,他们都是Functors!例如。:好的。

1
2
3
instance Functor MonoidArgument where
  fmap f (Mappend τ τ) = Mappend (f τ) (f τ)
  fmap f Mempty        = Mempty

所以我们可以更广泛地推广我们的代数思想。它只是一些类型的τ,对于一些函数f,它有一个函数f τ → τ。实际上,我们可以把它写成一个类型类:好的。

1
2
class Functor f ? Algebra f τ where
  op ∷ f τ → τ

这通常被称为"F代数",因为它是由函子f决定的。如果我们可以部分地应用类型词,我们可以定义一些类似EDCOX1的5。好的。余代数

现在,希望你能很好地了解代数是什么,它只是普通代数结构的一个推广。那么什么是F-coalgebra?好吧,co意味着它是一个代数的"对偶",也就是说,我们取一个代数并翻转一些箭头。我在上面的定义中只看到一个箭头,所以我将翻转它:好的。

1
2
class Functor f ? CoAlgebra f τ where
  coop ∷ τ → f τ

就这样!现在,这个结论似乎有点轻率。它告诉你什么是一个coalgebra,但并不真正给任何洞察它是如何有用或为什么我们关心。一旦我找到或想出一个或两个好的例子,我将在一点后讨论这个问题:p。好的。类和对象

在阅读了一点之后,我想我对如何使用coalgebras来表示类和对象有了一个很好的了解。我们有一个包含类中对象所有可能的内部状态的C类型;类本身是C之上的coalgebra,它指定了对象的方法和属性。好的。

如代数例子所示,如果对于任何一个a, b,…,我们有一组函数,如a → τb → τ,我们可以使用Either,一个和类型,将它们组合成一个函数。双重"概念"将结合一系列τ → a型、τ → b型等功能。我们可以使用求和类型A产品类型的对偶来实现这一点。因此,考虑到上述两个功能(称为fg),我们可以创建一个这样的功能:好的。

1
2
both ∷ τ → (a, b)
both x = (f x, g x)

类型(a, a)是一个简单的函数,因此它当然符合我们关于f-余代数的概念。这个特殊的技巧让我们将一系列不同的函数或者OOP方法打包成一个τ → f τ类型的函数。好的。

我们类型C的元素表示对象的内部状态。如果对象有一些可读属性,它们必须能够依赖于状态。最明显的方法是使它们成为C的函数。因此,如果我们想要一个长度属性(例如object.length),我们将有一个函数C → Int。好的。

我们需要能够接受参数并修改状态的方法。要做到这一点,我们需要采取所有的论点,并产生一个新的C。让我们想象一个setPosition方法,它采用xy坐标:object.setPosition(1, 2)。它应该是这样的:C → ((Int, Int) → C)。好的。

这里的重要模式是,对象的"方法"和"属性"将对象本身作为第一个参数。这就像python中的self参数和许多其他语言中的隐式this参数一样。coalgebra基本上只是封装了接受self参数的行为:这就是C → F C中的第一个C参数。好的。

所以让我们把它们放在一起。让我们设想一个具有position属性、name属性和setPosition函数的类:好的。

1
2
3
4
5
6
7
8
class C
  private
    x, y  : Int
    _name : String
  public
    name        : String
    position    : (Int, Int)
    setPosition : (Int, Int) → C

我们需要两个部分来表示这个类。首先,我们需要表示对象的内部状态;在这种情况下,它只包含两个Ints和一个String。(这是我们的C型)然后我们需要提出代表这个类的coalgebra。好的。

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 }

这就像一个带有显式self变量的python类。现在我们有了一组self →函数,我们需要将它们组合成一个coalgebra函数。我们可以用一个简单的元组来实现这一点:好的。

1
2
coop ∷ C → ((Int, Int), String, (Int, Int) → C)
coop self = (position self, name self, setPosition self)

对于任何C类型的((Int, Int), String, (Int, Int) → c)是一个函数,所以coop的形式是我们想要的:Functor f ? C → f C。好的。

鉴于此,Ccoop组成了一个coalgebra,指定了上面给出的I类。您可以看到我们如何使用相同的技术来为对象指定任意数量的方法和属性。好的。

这允许我们使用coalgebraic推理来处理类。例如,我们可以引入"F-余代数同态"的概念来表示类之间的转换。这是一个听起来吓人的术语,仅仅意味着保留结构的煤壶菌之间的转变。这使得考虑将类映射到其他类变得更加容易。好的。

简而言之,F-coalgebra通过拥有一系列属性和方法来表示类,这些属性和方法都依赖于包含每个对象内部状态的self参数。好的。其他类别

到目前为止,我们已经讨论过代数和代数作为haskell类型。代数只是一个具有函数f τ → ττ型,coalgebra只是一个具有函数τ → f ττ型。好的。

然而,没有什么能真正把这些想法与哈斯克尔联系起来。实际上,它们通常是用集合和数学函数来介绍的,而不是用类型和haskell函数。事实上,我们可以将这些概念推广到任何类别!好的。

对于某些范畴C,我们可以定义一个F代数。首先,我们需要一个函子F : C → C-即,一个内括约肌。(所有Haskell Functors实际上是Hask → Hask中的内生器。),代数只是C中的一个对象A,具有态射F A → A。余代数是相同的,除了A → F A。好的。

考虑到其他类别,我们能得到什么?好吧,我们可以在不同的环境中使用相同的想法。像单子一样。在haskell中,monad是一种类型的M ∷ → ,有三种操作:好的。

1
2
3
map      ∷ (α → β)(M α → M β)
return   ∷ α → M α
join     ∷ M (M α) → M α

map函数只是MFunctor的事实的证明。所以我们可以说一个monad只是一个有两个操作的函数:returnjoin。好的。

函子本身形成一个范畴,它们之间的同构就是所谓的"自然变换"。自然变换只是将一个函子变换成另一个函子,同时保留其结构的一种方法。这是一篇很好的文章,有助于解释这个想法。它谈论的是concat,只是清单的join。好的。

对于haskell函子,两个函子的组合本身就是一个函子。在伪代码中,我们可以写下:好的。

1
2
instance (Functor f, Functor g) ? Functor (f ° g) where
  fmap fun x = fmap (fmap fun) x

这有助于我们将join视为来自f ° f → f的映射。join的类型是?α. f (f α) → f α。直观地说,我们可以看到一个对所有类型的α有效的函数如何被认为是f的转换。好的。

return是一个类似的转变。它的类型是?α. α → f α。这看起来不同,第一个α不是"in"函子!令人高兴的是,我们可以通过在其上增加一个恒等函子来解决这个问题:?α. Identity α → f α。因此return是EDOCX1的一个变换19。好的。

现在我们可以把一个单子看作是一个基于一些函子f的代数,它的运算是f ° f → f和EDCX1〔19〕。这看起来不熟悉吗?它非常类似于一个单体,它只是一个类型的τ,操作τ × τ → τ() → τ。好的。

所以monad就像一个monoid,除了我们有一个函数而不是一个类型。它是同一类代数,只是属于不同的范畴。(据我所知,"一个单子只是内函数类中的一个单倍体"这个短语就是从这里来的。)好的。

现在,我们有两个操作:f ° f → fIdentity → f。为了得到相应的coalgebra,我们只需翻转箭头。这给了我们两个新的业务:f → f ° ff → Identity。我们可以通过如上所述添加类型变量将它们转换为haskell类型,从而得到?α. f α → f (f α)?α. f α → α。这看起来就像是一个科摩纳达的定义:好的。

1
2
3
class Functor f ? Comonad f where
  coreturn ∷ f α → α
  cojoin   ∷ f α → f (f α)

所以一个辅酶就是一个内功能子。好的。好啊。


F-代数和F-余代数是数学结构,有助于归纳类型(或递归类型)的推理。好的。F-代数

我们先从F-代数开始。我会尽量简单。好的。

我想你知道什么是递归类型。例如,这是整数列表的类型:好的。

1
data IntList = Nil | Cons (Int, IntList)

很明显,它是递归的——实际上,它的定义是指它自己。它的定义由两个数据构造函数组成,它们具有以下类型:好的。

1
2
Nil  :: () -> IntList
Cons :: (Int, IntList) -> IntList

注意,我把Nil的类型写成() -> IntList,而不仅仅是IntList。从理论上讲,它们实际上是等价的类型,因为()类型只有一个居民。好的。

如果我们用一种更为固定的理论方法来写这些函数的签名,我们将得到好的。

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

其中1是一个单元集(用一个元素集),A × B运算是两个集合AB的叉积(即对(a, b)的集合,其中A通过A的所有元素,B通过B的所有元素)。好的。

两套AB不相交的联合是一套A | B,是一套{(a, 1) : a in A}{(b, 2) : b in B}的联合。从本质上讲,它是来自AB的所有元素的集合,但每个元素都"标记"为属于AB,因此当我们从A | B中选择任何元素时,我们将立即知道该元素是来自A还是来自B。好的。

我们可以"加入"NilCons函数,因此它们将形成一个在集合1 | (Int × IntList)上工作的单一函数:好的。

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

实际上,如果Nil|Cons函数应用于()值(显然,它属于1 | (Int × IntList)集),那么它的行为就好像是Nil一样;如果Nil|Cons应用于(Int, IntList)类型的任何值(这些值也在1 | (Int × IntList)集中),那么它的行为就好像是Cons一样。好的。

现在考虑另一个数据类型:好的。

1
data IntTree = Leaf Int | Branch (IntTree, IntTree)

它有以下构造函数:好的。

1
2
Leaf   :: Int -> IntTree
Branch :: (IntTree, IntTree) -> IntTree

也可加入一个功能:好的。

1
Leaf|Branch :: Int | (IntTree × IntTree) -> IntTree

可以看出,这两个joined函数都有类似的类型:它们看起来都像好的。

1
f :: F T -> T

其中F是一类变换,它取了我们的类型,给出了更复杂的类型,它由EDCX1〔38〕和EDCX1〔39〕运算,EDCX1〔40〕的用法和可能的其它类型组成。例如,对于EDCOX1,2,EDCX1,42,EDCOX1,37,它看起来如下:好的。

1
2
F1 T = 1 | (Int × T)
F2 T = Int | (T × T)

我们可以立即注意到,任何代数类型都可以用这种方式写成。事实上,这就是为什么他们被称为"代数":他们由一些"总和"(工会)和"产品"(交叉产品)的其他类型。好的。

现在我们可以定义f-代数。F-代数只是一对(T, f),其中T是某种类型,ff :: F T -> T类型的函数。在我们的例子中,F-代数是(IntList, Nil|Cons)(IntTree, Leaf|Branch)。但是请注意,尽管每个f的f函数类型相同,但Tf本身可以是任意的。例如,对于某些gh来说,(String, g :: 1 | (Int x String) -> String)(Double, h :: Int | (Double, Double) -> Double)也是对应f的f-代数。好的。

然后我们可以引入F-代数同态,然后引入初始的F-代数,它们具有非常有用的性质。实际上,(IntList, Nil|Cons)是一个初始f1代数,(IntTree, Leaf|Branch)是一个初始f1代数。我不会给出这些术语和属性的确切定义,因为它们比需要的更复杂和抽象。好的。

尽管如此,比如说,(IntList, Nil|Cons)是f-代数的事实允许我们在这种类型上定义fold样的函数。如您所知,fold是一种将某些递归数据类型转换为一个有限值的操作。例如,我们可以将整数列表折叠为单个值,该值是列表中所有元素的总和:好的。

1
foldr (+) 0 [1, 2, 3, 4] -> 1 + 2 + 3 + 4 = 10

可以将这种操作推广到任何递归数据类型上。好的。

以下是foldr函数的签名:好的。

1
foldr :: ((a -> b -> b), b) -> [a] -> b

注意,我使用大括号将前两个参数与后一个参数分开。这不是真正的foldr函数,但它是同构的(也就是说,你可以很容易地从另一个函数中得到一个函数,反之亦然)。部分应用的foldr将具有以下签名:好的。

1
foldr ((+), 0) :: [Int] -> Int

我们可以看到这是一个函数,它接受一个整数列表并返回一个整数。让我们用我们的IntList类型来定义这个函数。好的。

1
2
3
sumFold :: IntList -> Int
sumFold Nil         = 0
sumFold (Cons x xs) = x + sumFold xs

我们看到,该函数由两部分组成:第一部分定义了该函数在IntListNil部分上的行为,第二部分定义了该函数在Cons部分上的行为。好的。

现在假设我们不是在Haskell编程,而是在某些语言中允许在类型签名中直接使用代数类型(技术上,Haskell允许通过元组和EDCOX1×24数据类型使用代数类型,但这将导致不必要的冗长)。考虑一个函数:好的。

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

可以看出,EDCOX1〔25〕是EDCOX1〔26〕类型的函数,正如F代数的定义一样。事实上,对(Int, reductor)是F1代数。好的。

由于IntList是一个初始的f1代数,对于每种类型的T和每种函数r :: F1 T -> T都存在一个函数,称为r的变形,它将IntList转换为T,这种函数是唯一的。实际上,在我们的例子中,reductor的变形是sumFold。请注意,reductorsumFold是如何相似的:它们的结构几乎相同!在reductor定义中,s参数用法(类型与T对应)对应于sumFold定义中sumFold xs计算结果的用法。好的。

为了使它更清晰并帮助您看到模式,这里是另一个例子,我们再次从产生的折叠函数开始。考虑将其第一个参数附加到第二个参数的append函数:好的。

1
(append [4, 5, 6]) [1, 2, 3] = (foldr (:) [4, 5, 6]) [1, 2, 3] -> [1, 2, 3, 4, 5, 6]

在我们的IntList上是这样看的:好的。

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

appendFoldappendReductor的一种变形,它将IntList转化为IntList。好的。

因此,本质上,F-代数允许我们在递归数据结构上定义"折叠",也就是说,将我们的结构减少到某个值的操作。好的。F-余代数

F-余代数是F-代数的所谓"对偶"项。它们允许我们为递归数据类型定义unfolds,也就是从某个值构造递归结构的方法。好的。

假设您有以下类型:好的。

1
data IntStream = Cons (Int, IntStream)

这是一个无限的整数流。它唯一的构造函数具有以下类型:好的。

1
Cons :: (Int, IntStream) -> IntStream

或者,就集合而言好的。

1
Cons :: Int × IntStream -> IntStream

haskell允许您在数据构造函数上进行模式匹配,因此您可以在IntStream上定义以下函数:好的。

1
2
3
4
5
head :: IntStream -> Int
head (Cons (x, xs)) = x

tail :: IntStream -> IntStream
tail (Cons (x, xs)) = xs

您可以自然地将这些函数"连接"到IntStream -> Int × IntStream类型的单个函数中:好的。

1
2
head&tail :: IntStream -> Int × IntStream
head&tail (Cons (x, xs)) = (x, xs)

注意函数的结果如何与我们的IntStream类型的代数表示一致。对于其他递归数据类型也可以执行类似的操作。也许你已经注意到了这个模式。我指的是一系列功能类型好的。

1
g :: T -> F T

其中T是某种类型。从现在开始我们将定义好的。

1
F1 T = Int × T

现在,f-coalgebra是一对(T, g),其中T是一个类型,gg :: T -> F T类型的函数。例如,(IntStream, head&tail)是f1 coalgebra。同样,正如在F-代数中,gT可以是任意的,例如,(String, h :: String -> Int x String)对于某些h也是f1 coalgebra。好的。

在所有的F-余代数中,有所谓的末端F-余代数,它是初始F-代数的对偶。例如,IntStream是一个终端F-coalgebra。这意味着对于每种类型的T和每个函数p :: T -> F1 T都存在一个称为变形的函数,它将T转换为IntStream,并且这种函数是唯一的。好的。

考虑以下函数,它从给定的整数开始生成一个连续整数流:好的。

1
2
nats :: Int -> IntStream
nats n = Cons (n, nats (n+1))

现在让我们检查一个函数natsBuilder :: Int -> F1 Int,也就是说,natsBuilder :: Int -> Int × Int:好的。

1
2
natsBuilder :: Int -> Int × Int
natsBuilder n = (n, n+1)

同样,我们可以看到natsnatsBuilder之间的一些相似之处。这与我们之前观察到的还原剂和褶皱的联系非常相似。natsnatsBuilder的变体。好的。

另一个例子是一个函数,它取一个值和一个函数,并将函数的连续应用流返回到值:好的。

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)

iterate是EDCX1〔31〕的变形。好的。结论

因此,简而言之,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
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.

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.htmlexamples of coalgebras by Alexandra Silva

将环境数学上下文与通常的编程任务相关联什么是"代数"?

代数结构通常看起来像:

  • 东西
  • 这些东西能做什么
  • 这听起来应该像是带有1的对象。属性和2。方法。或者更好的是,它听起来应该像类型签名。

    标准的数学例子包括monoid?小组?矢量空间?"代数。单倍体就像自动机:动词序列(如f.g.h.h.nothing.f.g.f)。一个git日志,它总是添加历史,从不删除历史,它将是一个monoid,而不是一个组。如果你加上倒数(如负数、分数、根、删除累积的历史记录、打开破碎的镜子),你就会得到一个组。

    组包含可以相加或相减的内容。例如,可以将Durations添加到一起。(但Dates不能。)持续时间存在于向量空间(不只是一个组),因为它们也可以由外部数字缩放。(scaling :: (Number,Duration) → Duration的类型签名。)

    Algebras?向量空间还可以做另一件事:有一些m :: (T,T) → T。称之为"乘法"或"不乘法",因为一旦离开Integers,就不太明显"乘法"(或"求幂")应该是什么。

    (这就是人们关注(范畴论)普遍属性的原因:告诉他们乘法应该做什么或应该是什么样子的:

    universal property of product)

    代数→代数

    与乘法相比,用一种感觉不武断的方式来定义Comultiplication更容易,因为从T → (T,T)开始,可以重复相同的元素。("对角线图"—类似于光谱理论中的对角线矩阵/运算符)

    counit通常是跟踪(对角线项的总和),但重要的是你的counit所做的;trace只是矩阵的一个很好的答案。

    一般来说,看到双重空间的原因是因为在这个空间中思考更容易。例如,有时考虑法向量比考虑它的法向量更容易,但是你可以用向量控制平面(包括超平面)(现在我说的是熟悉的几何向量,就像在光线跟踪器中一样)。

    驯服(非)结构化数据

    数学家们可能正在为一些有趣的东西建模,比如TQFT,而程序员则不得不与之抗衡。

    • 日期/时间(+ :: (Date,Duration) → Date)
    • 地点(Paris≠(+48.8567,+2.3508)!这是一个形状,不是一个点。)
    • 非结构化JSON,在某种意义上应该是一致的,
    • 错误但关闭XML,
    • 极其复杂的地理信息系统数据,应满足合理关系的负荷,
    • 正则表达式对您有意义,但对Perl的意义要小得多。
    • 客户关系管理系统,应包含所有行政人员的电话号码和别墅位置、他(现在的前)妻子和孩子的姓名、生日和所有以前的礼物,每个礼物都应满足"明显"的关系(对客户来说是显而易见的),这些关系极难编码。

    计算机科学家在谈论代数时,通常都会想到一些操作,比如笛卡尔产品。我相信这就是人们所说的"代数是哈斯克尔的余代数"的意思。但是,在一定程度上,程序员必须对复杂的数据类型(如PlaceDate/TimeCustomer)建模,并使这些模型看起来尽可能像真实世界(或至少是最终用户对真实世界的看法),我相信duals,可能比设置世界更有用。