我听到这个词“余代数”多次在函数式编程和PLT界,尤其是当讨论的是对象,comonads,镜头和这样的。 谷歌搜索这个词给人,给这些结构是非常难以理解的我的数学描述页面。 任何人都可以请解释一下余代数在编程方面的意思,什么是他们的意义,以及它们如何与对象和comonads?
Answer 1:
代数
我想开始将理解的代数的想法的地方。 这仅仅是一个像群,环,幺等代数结构的概括。 大多数时候,这些东西都是以成套方面介绍,但因为我们是和朋友在一起,我将谈谈Haskell的类型,而不是。 (我无法抗拒使用一些希腊字母,虽然,他们让一切看起来冷却器!)
代数,那么,仅仅是一个类型τ
的一些功能和身份。 这些函数的参数类型的不同数量τ
,并产生一个τ
:uncurried,他们看起来都像(τ, τ,…, τ) → τ
。 他们还可以有“身份” -elements τ
是有一些功能特殊行为。
这个最简单的例子就是幺。 幺半群是任何类型的τ
与函数mappend ∷ (τ, τ) → τ
和身份mzero ∷ τ
。 其他的例子包括组的事情(这是类似,只是一个额外的类群invert ∷ τ → τ
功能),戒指,格子等。
所有的功能进行操作τ
,但可以有不同的arities。 我们可以写这些出来作为τⁿ → τ
,其中τⁿ
映射到的元组n
τ
。 通过这种方式,它是有道理认为身份为τ⁰ → τ
其中τ⁰
只是空的元组()
所以我们实际上可以简化现在的代数的想法:这只是某种类型的一些数量上的功能。
代数仅仅是在一个已经“分解出来”数学一个共同的模式,就像我们做的代码。 人们注意到有趣的事情,上述类群,组格等等,所有一大堆遵循类似的模式,所以他们抽象出来。 这样做的好处是一样的编程:它创建可重复使用的证据,使某些类型的推理更加容易。
F-代数
但是,我们还没有与保完成。 到目前为止,我们有一大堆的功能τⁿ → τ
。 事实上,我们可以做一个巧妙的方法将它们全部组合成一个功能。 尤其是,让我们来看看类群:我们mappend ∷ (τ, τ) → τ
和mempty ∷ () → τ
。 我们可以使用类型A和把这些成一个单一的功能, Either
。 它是这样的:
op ∷ Monoid τ ⇒ Either (τ, τ) () → τ
op (Left (a, b)) = mappend (a, b)
op (Right ()) = mempty
我们可以使用这种转变多次向所有集约τⁿ → τ
功能集成到一个单一的一个,对于任何代数。 (事实上,我们可以为任意数量的功能做a → τ
, b → τ
等任何 a, b,…
。)
这让我们谈论代数作为一种τ
从一些乱七八糟的单一功能Either
s到一个单一的τ
。 对于类群,这种混乱是: Either (τ, τ) ()
; 团体(其中有一个额外的τ → τ
操作),它是: Either (Either (τ, τ) τ) ()
这是一个不同类型,为每个不同的结构。 那么怎么所有这些类型有什么共同点? 最明显的一点是,他们都只是产品代数数据类型的总和。 例如,对于类群,我们可以创建任何幺τ工作的幺参数类型:
data MonoidArgument τ = Mappend τ τ -- here τ τ is the same as (τ, τ)
| Mempty -- here we can just leave the () out
我们可以做的团体和吊环和晶格和所有其他可能的结构是一样的。
还有什么特别之处所有这些类型的? 那么,他们都Functors
! 例如:
instance Functor MonoidArgument where
fmap f (Mappend τ τ) = Mappend (f τ) (f τ)
fmap f Mempty = Mempty
因此,我们可以更概括我们的代数的想法。 这只是某种类型τ
与函数f τ → τ
一些仿函数f
。 事实上,我们可以写这个作为一个类型类:
class Functor f ⇒ Algebra f τ where
op ∷ f τ → τ
这通常被称为“F-代数”,因为它是由仿函数确定F
。 如果我们能部分适用类型类,我们可以定义类似class Monoid = Algebra MonoidArgument
。
余代数
现在,希望你有一个代数就是一个很好的把握,以及它如何只是一个正常的代数结构的概括。 那么,什么是F-余代数? 好吧,共意味着它是一个代数,那就是“双反”,我们采取的代数和倒装一些箭头。 我只看到在上面定义一个箭头,所以我就翻转是:
class Functor f ⇒ CoAlgebra f τ where
coop ∷ τ → f τ
而这一切,它是! 现在,这个结论似乎有点轻率(嘿嘿)。 它会告诉你一个余代数是什么 ,但并没有真正给它如何是非常有用的,或者为什么我们关心的任何见解。 我会得到在一点,一旦我找到或拿出一个很好的例子或两个:P。
类与对象
逛了一下看完后,我觉得我有一个如何使用余代数表示类和对象是个好主意。 我们有一个类型C
包含在类对象的所有可能的内部状态; 类本身是在余代数C
它指定了方法和对象的属性。
如图中代数例如,如果我们有像一组函数a → τ
和b → τ
为任何a, b,…
,我们可以将它们都合并到使用单个函数Either
,总和类型。 双重“概念”将被组合一堆类型的函数τ → a
, τ → b
等。 我们可以使用双一笔类型产品类型的做到这一点。 因此,考虑上述(称为两个函数f
和g
),我们可以建立一个单一的一个像这样:
both ∷ τ → (a, b)
both x = (f x, g x)
类型(a, a)
是在简单的方式仿函数,所以它肯定是与我们的F-余代数的概念相符。 这个特定的特技让我们打包一堆不同的功能,或者,为OOP,方法-成类型的单个函数τ → f τ
。
我们的类型的元素C
表示对象的内部状态。 如果对象有一些可读的属性,他们必须要能够依靠国家。 最明显的方式做到这一点是让他们的功能C
。 因此,如果我们希望有一个length属性(如object.length
),我们将有一个功能C → Int
。
我们希望可以带参数和修改状态的方法。 要做到这一点,我们需要把所有的参数,并产生一个新的C
。 让我们设想一个setPosition
方法它接受一个x
和y
坐标: object.setPosition(1, 2)
它是这样的: C → ((Int, Int) → C)
这里最重要的模式是在对象的“方法”和“属性”拍摄物体本身作为他们的第一个参数。 这就像self
在Python参数和喜欢的隐性this
许多其他语言的。 一个余代数基本上只是封装的拍摄行为self
参数:这是第什么C
在C → FC
是。
因此,让我们把它放在一起。 让我们想象有一类position
属性,一个name
属性和setPosition
功能:
class C
private
x, y : Int
_name : String
public
name : String
position : (Int, Int)
setPosition : (Int, Int) → C
我们需要两个部分来表示这个类。 首先,我们要表示对象的内部状态; 在这种情况下,它仅仅拥有两个Int
S和一个String
。 (这是我们的类型C
。)然后,我们需要拿出表示类的余代数。
data C = Obj { x, y ∷ Int
, _name ∷ String }
我们有两个属性来编写。 他们是非常容易的:
position ∷ C → (Int, Int)
position self = (x self, y self)
name ∷ C → String
name self = _name self
现在,我们只需要能够更新的位置:
setPosition ∷ C → (Int, Int) → C
setPosition self (newX, newY) = self { x = newX, y = newY }
这就像有其明确的Python类self
变量。 现在,我们有一大堆的self →
功能,我们需要将它们组合成的余代数的单一功能。 我们可以用一个简单的元组这样做:
coop ∷ C → ((Int, Int), String, (Int, Int) → C)
coop self = (position self, name self, setPosition self)
类型((Int, Int), String, (Int, Int) → c)
-用于任何 c
-is算符,所以coop
确实有我们想要的形式: Functor f ⇒ C → f C
。
鉴于此, C
随着coop
形成余代数指定我上面给的类。 你可以看到,我们如何可以使用同样的技术来指定任意数量的方法和属性,我们的对象有。
这让我们使用coalgebraic推理来处理类。 例如,我们可以在“F-余代数同态”来表示类之间转换的概念带来的。 这是一个可怕的冠冕堂皇的术语,它只是意味着保持结构余代数之间的转换。 这使得去想映射类更容易在其他类。
简而言之,F-余代数表示由具有一堆属性和方法都依赖于一个的一类self
包含每个对象的内部状态的参数。
其他类别
到目前为止,我们已经讨论了代数和余代数作为哈斯克尔类型。 一个代数仅仅是一个类型τ
与函数f τ → τ
和一个余代数只是一个类型τ
与函数τ → f τ
。
然而,没有什么领带这些想法的Haskell 本身 。 事实上,他们通常以成套条款和数学函数而非类型和Haskell功能介绍。 事实上,我们可以概括这些概念对任何类别!
我们可以定义一个F-代数对于一些类别C
。 首先,我们需要一个函子F : C → C
也就是说,一个endofunctor。 (所有的Haskell Functor
s为实际从endofunctors Hask → Hask
)。然后,一个代数只是一个对象A
从C
与态射FA → A
甲余代数是除了用相同的A → FA
。
什么是我们考虑其他类别获得什么? 那么,我们就可以在不同的情况下使用相同的想法。 像单子。 在Haskell中,一个单子是某种类型的M ∷ ★ → ★
用三种操作:
map ∷ (α → β) → (M α → M β)
return ∷ α → M α
join ∷ M (M α) → M α
该map
功能仅仅是一个事实,证明M
是一个Functor
。 因此,我们可以说,一个单子只是一个带有两个操作算符: return
和join
。
函子形成了类别自己,用被所谓的“自然变换”他们之间的同态。 一个自然的改造只是将一种仿成另一种,同时保留它的结构方式。 这里有一个很好的文章,帮助解释的想法。 它谈论concat
,这是刚刚join
的名单。
与Haskell的函子中,两个函子的组合物是一个算符本身。 在伪代码,我们可以这样写:
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 (f α) → f α
。 直观地说,我们可以看到一个功能适用于所有类型的α
可以被认为是一个转型f
。
return
是一个类似的转变。 它的类型是∀α. α → f α
∀α. α → f α
。 这看起来不同,第一α
不是“中的”函子! :令人高兴的是,我们可以通过添加一个身份函子有解决这个问题∀α. Identity α → f α
∀α. Identity α → f α
。 所以return
是一个转变Identity → f
。
现在我们可以思考一个单子的正是基于周围的一些仿函数的代数f
与操作f ∘ f → f
和Identity → f
。 这难道不是很熟悉? 这是非常相似的独异,这只是某种类型的τ
与操作τ × τ → τ
和() → τ
。
因此,一个单子就像一个独异,但不是有型,我们有一个仿函数。 这是同一类代数的,只是在不同的类别。 (这就是那句“一个单子就是在endofunctors类别幺半群”的由来,据我所知)。
现在,我们有这两个操作: f ∘ f → f
和Identity → f
。 要获得相应的余代数,我们只是翻转箭头。 这给了我们两个新的操作: f → f ∘ f
和f → Identity
。 我们可以通过添加类型变量如上,让我们把它们变成哈斯克尔类型∀α. f α → f (f α)
∀α. f α → f (f α)
和∀α. f α → α
∀α. f α → α
。 这看起来就像一个comonad的定义:
class Functor f ⇒ Comonad f where
coreturn ∷ f α → α
cojoin ∷ f α → f (f α)
所以comonad然后在endofunctors的类别余代数 。
Answer 2:
F-代数和F-余代数是数学结构,该结构在约归纳类型 (或递归类型 )推理工具。
F-代数
我们将与F-代数第一次启动。 我会尝试尽可能简单。
我猜你知道什么是递归的类型。 例如,这是一个类型整数列表:
data IntList = Nil | Cons (Int, IntList)
很明显,这是递归的 - 事实上,它的定义是指本身。 它的定义由两个数据构造,它有以下类型:
Nil :: () -> IntList
Cons :: (Int, IntList) -> IntList
请注意,我写的类型Nil
为() -> IntList
,不是简单地IntList
。 这些都是在实际上从理论角度等效类型,因为()
类型只有一个居民。
如果我们写的这些功能特征更集合论的方式,我们将得到
Nil :: 1 -> IntList
Cons :: Int × IntList -> IntList
其中1
是设定的单元(与一个元件组)和A × B
的操作是两组的矢量积A
和B
(即,设置对(a, b)
其中a
经过的所有元素A
和b
变为通过的所有元素B
)。
两套不交A
和B
是一组A | B
A | B
这是集的并集{(a, 1) : a in A}
和{(b, 2) : b in B}
本质上,它是一组来自两个的所有元素的A
和B
的,但与每个这种元素的被标记为“为属于任一的A
或B
,所以当我们挑选任何元件从A | B
A | B
我们会马上知道这个元素是否是从哪里来的A
或B
。
我们可以“加入” Nil
和Cons
的功能,所以他们会形成一个单一的功能上的一组工作1 | (Int × IntList)
1 | (Int × IntList)
:
Nil|Cons :: 1 | (Int × IntList) -> IntList
事实上,如果Nil|Cons
函数应用于()
值(其很明显,属于1 | (Int × IntList)
组),那么它的行为就好像它是Nil
; 如果Nil|Cons
被施加到类型的任何值(Int, IntList)
(这些值也可在组1 | (Int × IntList)
,它表现为Cons
。
现在考虑另一种数据类型:
data IntTree = Leaf Int | Branch (IntTree, IntTree)
它具有以下构造函数:
Leaf :: Int -> IntTree
Branch :: (IntTree, IntTree) -> IntTree
这也可以结合成一个函数:
Leaf|Branch :: Int | (IntTree × IntTree) -> IntTree
由此可以看出,两者的这种joined
的功能有类似的类型:他们都看起来像
f :: F T -> T
其中F
是一种转型这需要我们的类型,并给出更复杂的类型,它由x
和|
操作,的用途T
和可能的其它类型。 例如,对于IntList
和IntTree
F
如下所示:
F1 T = 1 | (Int × T)
F2 T = Int | (T × T)
我们可以立即发现任何代数类型可以用这种方式来写。 事实上,这就是为什么他们被称为“代数”:它们是由许多“总和”(工会)和“产品”的其他类型的(跨产品)的。
现在,我们可以定义F-代数。 F-代数仅仅是一个对(T, f)
其中T
是某种类型和f
是类型的函数f :: FT -> T
。 在我们的例子F-代数分别是(IntList, Nil|Cons)
和(IntTree, Leaf|Branch)
。 但是请注意,尽管该类型的f
功能是相同的每个楼T
和f
本身可以是任意的。 例如, (String, g :: 1 | (Int x String) -> String)
或(Double, h :: Int | (Double, Double) -> Double)
对于一些g
和h
也F-代数对应F。
以后我们就可以引进F-代数同态 ,然后初始F-代数 ,它具有非常有用的特性。 事实上, (IntList, Nil|Cons)
是初始F1-代数,和(IntTree, Leaf|Branch)
是初始F2-代数。 我不会出现这些条款和性质的确切定义,因为它们是更为复杂和抽象的比需要。
然而,事实证明,说, (IntList, Nil|Cons)
是F-代数允许我们定义fold
式的功能这种类型。 如你所知,折叠是一种操作,其将一些递归数据类型在一个有限值。 例如,我们可以整数的列表折叠成一个单一的值,它是在列表中的所有元素的总和:
foldr (+) 0 [1, 2, 3, 4] -> 1 + 2 + 3 + 4 = 10
它可以对任何数据类型的递归概括这样的操作。
下面是签名foldr
的功能:
foldr :: ((a -> b -> b), b) -> [a] -> b
请注意,我用大括号前两个参数从最后一个分离。 这不是真正的foldr
的功能,但它是同构于它(也就是说,你可以很容易地从另一个反之亦然)。 部分应用foldr
将具有以下特征:
foldr ((+), 0) :: [Int] -> Int
我们可以看到,这是一个函数,它接受一个整数列表,并返回一个整数。 让我们来定义这样的功能IntList
的类型。
sumFold :: IntList -> Int
sumFold Nil = 0
sumFold (Cons x xs) = x + sumFold xs
我们看到,这个功能由两个部分组成:第一部分定义此函数的行为上Nil
的一部分IntList
,和第二部分定义函数的行为上的Cons
一部分。
现在假设我们不编程在Haskell,但在某些语言,它允许代数类型直接在类型签名的使用(当然,技术上的Haskell允许代数类型的使用通过元组和Either ab
数据类型,但是这会导致不必要的冗长)。 考虑一个函数:
reductor :: () | (Int × Int) -> Int
reductor () = 0
reductor (x, s) = x + s
由此可以看出, reductor
是类型的函数F1 Int -> Int
,就像在F-代数的定义! 实际上,一对(Int, reductor)
是F1-代数。
因为IntList
是初始F1-代数,对于每种类型T
和每个功能r :: F1 T -> T
存在的函数,称为catamorphism为r
它转换IntList
至T
,并且这样的功能是唯一的。 事实上,在我们的例子为catamorphism reductor
时sumFold
。 注意如何reductor
和sumFold
是相似的:它们有几乎相同的结构! 在reductor
定义s
参数用法(其类型对应于T
)对应于计算结果的使用sumFold xs
在sumFold
定义。
只是为了更清楚,并帮助你看到的模式,这里是另外一个例子,我们再从得到的折叠功能开始。 考虑append
功能,追加了第一个参数第二个:
(append [4, 5, 6]) [1, 2, 3] = (foldr (:) [4, 5, 6]) [1, 2, 3] -> [1, 2, 3, 4, 5, 6]
这究竟是怎么看我们的IntList
:
appendFold :: IntList -> IntList -> IntList
appendFold ys () = ys
appendFold ys (Cons x xs) = x : appendFold ys xs
再次,让我们试着写出来的减速机:
appendReductor :: IntList -> () | (Int × IntList) -> IntList
appendReductor ys () = ys
appendReductor ys (x, rs) = x : rs
appendFold
为catamorphism appendReductor
其将IntList
成IntList
。
因此,从本质上讲,F-代数允许我们定义递归数据结构“折叠”,即,从而降低我们的结构在一定值的操作。
F-余代数
F-余代数是所谓的“双反”期限为F-代数。 他们允许我们定义unfolds
递归数据类型,也就是一种方法来构建一些价值递归结构。
假设你有以下类型:
data IntStream = Cons (Int, IntStream)
这是一个整数的无限流。 它唯一的构造方法有以下类型:
Cons :: (Int, IntStream) -> IntStream
或者,以成套条款
Cons :: Int × IntStream -> IntStream
哈斯克尔允许您在数据构造的模式匹配,这样你就可以定义工作,则下列功能IntStream
S:
head :: IntStream -> Int
head (Cons (x, xs)) = x
tail :: IntStream -> IntStream
tail (Cons (x, xs)) = xs
你自然也“加入”这些功能为类型的单一功能IntStream -> Int × IntStream
:
head&tail :: IntStream -> Int × IntStream
head&tail (Cons (x, xs)) = (x, xs)
注意函数的结果如何与我们的代数表示一致IntStream
类型。 类似的事情也可以用于其它递归类型实现。 也许你已经注意到了模式。 我指的是一个家庭型的功能
g :: T -> F T
其中T
是某种类型的。 从现在开始,我们将定义
F1 T = Int × T
现在,F-余代数是一对(T, g)
其中T
是一个类型, g
是类型的函数g :: T -> FT
。 例如, (IntStream, head&tail)
是F1-余代数。 同样,正如在F-代数, g
和T
可以是任意的,例如, (String, h :: String -> Int x String)
也是一种F1-余代数一些小时。
在所有F-余代数有所谓的终端F-余代数 ,其是双初始F-代数。 例如, IntStream
是终端F-余代数。 这意味着,对于每一种类型的T
和为每个函数p :: T -> F1 T
存在的函数,称为anamorphism,它转换T
到IntStream
,并且这样的功能是唯一的。
考虑下面的函数,其产生从给定的一个开始的连续整数的流:
nats :: Int -> IntStream
nats n = Cons (n, nats (n+1))
现在,让我们检查的功能natsBuilder :: Int -> F1 Int
,那就是, natsBuilder :: Int -> Int × Int
:
natsBuilder :: Int -> Int × Int
natsBuilder n = (n, n+1)
同样,我们可以看到的一些相似nats
和natsBuilder
。 这是非常相似,我们已经与减压器观察和早期折叠连接。 nats
是一个anamorphism natsBuilder
。
另一实例中,这需要一个值和一个函数,并返回的功能的值的连续应用的流的函数:
iterate :: (Int -> Int) -> Int -> IntStream
iterate f n = Cons (n, iterate f (f n))
它的建造功能是以下之一:
iterateBuilder :: (Int -> Int) -> Int -> Int × Int
iterateBuilder f n = (n, f n)
然后iterate
是一个anamorphism iterateBuilder
。
结论
因此,简而言之,F-代数允许定义折叠,即,它向下减少递归结构成一个单一的值的操作,和F-余代数允许做相反的:从一个单一的值构建[潜在]无限结构。
事实上在Haskell F-代数和F-余代数一致。 这是一个非常好的特性是在每种类型的“底部”值的存在的结果。 所以在Haskell既折叠和展开可以为每个递归式创建。 然而,这背后的理论模型比我上面提出的一个比较复杂的,所以我刻意避免了它。
希望这可以帮助。
Answer 3:
通过本教程纸去上(共)代数的讲解和(共)感应应该给你的计算机科学合作代数的一些见解。
下面是它的一个引用来说服你,
总体而言,在一些编程语言编写的程序处理数据。 在计算机科学在过去几十年的发展很显然,这些数据的抽象描述是可取的,例如,以确保自己的程序不取决于在其经营数据的特定表示。 此外,这种抽象简化的正确性证明。
这种欲望导致使用的计算机科学代数方法,在一个叫做代数规范或抽象数据类型理论分支。 研究的对象是其本身的数据类型,使用的是其中从代数熟悉的技术概念。 计算机科学家使用的数据类型通常是由(构造函数)操作的给定集合产生的,它是这个原因,代数“initiality”中扮演如此重要的角色。
标准的代数技术已经被证明在捕捉在计算机科学中使用的数据结构的各个重要方面是有用的。 但它被证明是困难的代数描述一些在计算存在的固有的动力学结构。 这样的结构通常包括状态的概念,它可以以各种方式进行变换。 正式的方法,例如基于状态的动力系统通常使用自动或过渡系统,古典早引用。
在过去十年的见解渐渐长大,这种基于状态的系统不应当被描述为代数,但作为所谓的共代数。 这些都是正式的双代数,其中将在本教程进行精确的方式。 “initiality”为代数的双重属性,即终局竟然是这样的合作代数至关重要。 那就是需要这样的最终共同代数的逻辑推理原则是不感应,但共同的诱导。
前奏,有关范畴论。 分类理论应该被重新命名函子的理论。 由于类别是什么人必须以定义仿函数定义。 (此外,仿函数是什么人必须以定义的自然转换定义。)
什么是仿函数? 这是一个从一组到另一哪些保护它们的结构转型。 (更多细节有在网上有很多很好的说明)。
什么是F-代数? 这是仿函数的代数。 这是仿函数的普遍礼刚研究。
怎么可以连接到计算机科学? 程序可以看作一个结构化的信息集合。 程序执行对应于这一结构化的信息集合的修改。 这听起来很好的执行应该保持程序结构。 然后执行可以是视图,作为函子放在该组的信息的应用程序。 (该一个定义程序)。
为什么F-CO-代数? 计划是由本质上双,因为它们是通过信息描述,他们采取行动。 然后,他们主要是改变了编写程序,使信息可以查看使用两个方法。
- 由程序处理的数据可以被定义为信息。
- 状态可以被定义为所述信息由节目共享。
然后,在这个阶段,我想这么说,
- F-代数是作用在数据的宇宙(如在这里定义的)函子改造的研究。
- F-CO-代数是作用于国家的宇宙(如在这里定义的)函子改造的研究。
在程序,数据和状态并存的生活,他们完成对方。 他们是双。
Answer 4:
我会用的东西,显然是编程相关的启动,然后添加一些数学的东西,以保持其作为混凝土和朴实的,我可以。
让我们引用的coinduction一些计算机科学家......
http://www.cs.umd.edu/~micinski/posts/2012-09-04-on-understanding-coinduction.html
感应是关于有限数据,共同诱导是大约无限数据。
无限数据的典型例子是懒列表(流)的类型。 例如,可以说,我们已经在内存中的下列对象:
let (pi : int list) = (* some function which computes the digits of
π. *)
计算机无法容纳所有π的,因为它只有一个内存有限的! 但是,什么可以做的是保持一个有限的计划,这将产生任何任意长π的扩张,你的愿望。 只要您只使用列表中的有限块,您可以用无限列表计算就像你所需要的。
然而,考虑下面的程序:
let print_third_element (k : int list) = match k with
| _ :: _ :: thd :: tl -> print thd
print_third_element pi
这个程序应该打印pi的第三位。 但在某些语言中,任何函数参数被传递给函数(严格,不懒惰,评估)之前评估。 如果我们使用这种降阶,那么我们上面的程序将运行永远计算圆周率的数字,才可以被传递给我们的打印机功能(这从未发生过)。 由于机器并没有无限的内存,程序将最终耗尽内存和崩溃。 这可能不是最好的评价顺序。
http://adam.chlipala.net/cpdt/html/Coinductive.html
在象Haskell懒函数式编程语言,无限数据结构是无处不在。 无限列表和更奇特的数据类型为程序的部件之间的通信提供便利的抽象。 没有无限慵懒结构实现了类似的便利时,在很多情况下,需要控制流量的杂技倒置。
http://www.alexandrasilva.org/#/talks.html
与周围的数学背景,以通常的编程任务
什么是“代数”?
代数结构一般是这样的:
- 东西
- 什么东西可以做
这应该听起来像1.属性和方法2.对象。 甚至更好,它应该听起来像类型签名。
标准的数学实例包括幺⊃组⊃向量空间⊃“代数”。 幺像自动机:动词的序列(例如, fghhnothing.fgf
)。 一个git
总是增加了历史,从来没有日志删除这将是一个独异而不是一组。 如果添加逆(例如负数,分数,根,删除积累的历史,未打破一个破碎的镜子),你会得到一组。
组包含可以增加或减去在一起的东西。 例如Duration
S可一起加入。 (但Date
s不能。)持续时间生活在一个向量空间(不只是一组),因为它们也可以通过外部的数字缩放。 (A型签名scaling :: (Number,Duration) → Duration
。)
代数⊂矢量空间可以做的另一件事:有一些m :: (T,T) → T
。 称之为“乘法”或者不这样做,因为一旦你离开Integers
它就是“乘法”(或不太明显的“幂”应该是)。
(这就是为什么人们期待(类别-理论)通用性:告诉他们什么乘法应该做的或者是这样 :
)
代数→余代数
余乘法是更容易,感觉非任意的方式来定义,比倍增,因为从去T → (T,T)
你可以重复相同的元素。 (“对角线地图” - 像对角矩阵/频谱理论运算符)
Counit通常是跟踪(对角线元素之和),虽然再有什么重要的是你counit 做什么; trace
仅仅是矩阵一个很好的答案。
之所以看一个双重空间 ,一般来说,是因为它更容易认为在这个空间。 例如它有时更容易想到比约是很正常的平面的法向量,但你可以控制与载体平面(包括超平面)(现在我在一个光线追踪讲熟悉的几何载体,等等) 。
驯服(UN)的结构化数据
数学家可能会造型好玩的东西像TQFT的 ,而程序员必须角力
- 日期/时间(
+ :: (Date,Duration) → Date
), - 地方(
Paris
≠(+48.8567,+2.3508)
这是一个形状,而不是一个点。) - 非结构化JSON这应该在某种意义上是一致的,
- 错了,但关闭XML,
- 极其复杂的GIS数据应满足合理的关系的负荷,
- 正则表达式这意味着什么给你,但意味着相当少到perl。
- CRM应保存所有主管的电话号码和别墅的位置,他(现在的EX-)的妻子和孩子的名字,生日和以前所有的礼物,每个应满足‘显而易见’的关系(明显的客户),这是令人难以置信硬编码了,
- .....
计算机科学家,说起余代数时,通常已经在心中设定上下的操作,如笛卡尔乘积。 我相信这是当他们说,如“代数是余代数在Haskell”什么人的意思。 但要程序员必须模拟复杂的数据类型,如程度Place
, Date/Time
,和Customer
-和使这些车型看起来就像真实世界(或至少是现实世界的最终用户的视图)尽可能 - 我相信偶,可能会超越只设置世界有用的。