Category Theory 随笔

什么可以帮助我们编程?

  • 抽象【Abstrction】:抽象【abstraction】一词来源于希腊语,和减法【substraction】的意思差不多 ,这意味着"摆脱不必要的细节"。一般当我们摆脱了不必要的细节,我们会发现那些原本不同的事物,突然变得一致起来(即恒等性【Identity】),这是因为这些事物是由于不必要的细节而显得不同罢了
  • 可组合性【Composability】:将复杂问题切分为多个简单问题,单独解决它们,然后将这些解决方案组合到一起
  • 恒等性【Identity】:Homotopy Type Theory,与同构【isomorphic】关系密切

注:Category Theory = Composability + Identity

面向对象的方法有一些问题,当人们开始编写并发代码和并行代码时,这种问题变得越来越明显 ,因此并发与面向对象编程不能很好地结合,这是为什么呢?

因为对象隐藏了实现,而他们隐藏的恰恰是两种非常重要的东西:

  • mutation:他们改变了一些内部状态,而我们并不知道,因为对象隐藏了它们,它并没有告诉我们它改变了一些东西
  • sharing:对象之间彼此共享数据

data race = mutation + sharing

面向对象中的抽象是建立在数据争用【data race】基础上的!!

范畴

范畴由对象【Object】 和箭头【Arrow】组成.

范畴公理:

  1. 组合性:f = a -> b,g  = b -> c,f  。g =  a - > c
  2. 恒等性:id = a -> a,f = a -> b, f  。id =  f = a -> b
  3. 结合性:f = a -> b,g  = b -> c,h = c -> d,h 。(g  。f ) = (h 。g ) 。f 

小范畴与大范畴:

  • 如果Objects构成集合【set】,则该范畴称为small category
  • 如果Objects并不构成集合【set】,则该范畴称为large category

范畴中 Objects 之间的 Arrow 可以构成集合【set】,但是也存在不构成集合的范畴,比如高阶范畴中,Arrows 就并不构成集合【sets】,而是构成一个范畴中的Objects。

在编程语言中,Objects就是Types,Arrows则是Functions:

  • Types:关于值的集合,因此我们以将编程建模为"在集合的范畴中"
  • Functions:类型之间的箭头

因此编程其实是关于Types和Functions的范畴,而Types和Functions的模型其实是集合【sets】以及集合之间的函数

每次你设计一种语言你都必须为该语言提供语义。有两种定义语义的方法:

  • 操作语义【operational semantics】,通过叙述如何执行来实现的,即你在定义语言的操作。你知道如何将一个表达式或语句转换成另一个更简单的表达式或语句,等等。  
  • 指称语义【denotational semantics】,你实际上是将它映射到其他你能理解领域,特别是映射到数学领域,所以你建立了一个数学模型,然后声明 "该语言中的这个语句【statement】或者这个结构【construct】,对应于数学上的一些东西,例如,Types,对应于值的集合【set】,而对于Functions,则对应于集合之间的态射【morphism】"。

偏函数【partial function】 VS 函数/全函数【total function】

  • 偏函数:为部分入参提供有效的结果,而对于其他参数,则可能会破坏计算机~~
  • 全函数:为所有的可能的入参返回有效的结果

如果判断一个函数是否是纯函数?答案:纯函数可以内存化【memoise】,这意味你可以将其转换为一个查找表【lookup table】,对于入参类型中的每个参数,都有其对应的值。

函数在数学中被定义为是一种特殊类型的关系【relation】。那么什么是关系【relation】呢?

答案:在集合论中,给定两个集合【set】,S1,S2,S1中的元素,与S2中的元素,进行笛卡尔积,得到了元素序对,元素序对的任何一个子集均构成关系【relation】,无需其他条件约束。

关系【relation】的一些性质:

  • 关系【relation】无需具有对称性,S1 中的元素与 S2 中的元素存在关系,但 S2 中的元素却不一定与 S1 中的元素存在关系
  • 关系【relation】不具有方向性【directionality】,但函数【function, ->】是具有方向的,这也是函数的一个很重要的性质。

那么我们给关系【relation】强加怎么样的约束,才可以使其变为函数【function】呢?

  1. 方向性【directionality】:在关系中,S1 中的同一个元素,可以与 S2 中任意多个元素组成关系,S1 中的多个元素亦可以与 S2 中的某一特定元素构成关系。但是在函数中,我们对前者进行约束,因此在函数中,S1 中的某一个元素只可以与 S2 中某一个元素存在映射,这也是函数具有方向性【directionality】性质的由来!!!
  2. 完全性【total】:为了实现全函数【total function】,S1 中的所有元素,必须均存在与 S2 中元素的映射关系,但是反之不然, S2 中的元素,并不一定要在 S1 中存在对立元素,即函数的像【Image】只是值域【Codomain】子集,这也是函数不具有对称性【symmetric】性质的由来!!!

因此,函数是有具有方向性【directionality】的,而且函数是不必具有对称性【symmetric】的

那么到底如何理解方向性【directionality】?最简单的方式就是提问 "该函数是可逆的吗?"  一般情况下它是不可逆的。譬如 f :: a -> b,如果该函数是可逆的,那么必然存在函数 g :: b -> a,保证  g  。f = id 且 f  。g = id,因此该函数便具有对称性【symmetirc】,即可逆函数自动地就具有对称性【symmetirc】。可逆函数也被称为同构【isomorphism】。

 

现在我们回到范畴论中,我们可以将同构【isomorphism】推广到任何范畴,无论是集合范畴还是拓扑范畴,因为同构【isomorphism】完全是由术语组合【Composition】和恒等【Inentity】表达构成,因此,如果 f 与 g 可以满足上述的关系,那么我们就可以称 f 是同构【isomorphism】的。

So : 同构【isomorphism】告诉你这两个东西在某种意义上是相同的 。

绝大部分函数并非同构,或者说绝大部分函数并非可逆的,原因有二:

  1. 函数可以折叠集合中的元素,即定义域【Domain】中多个元素可以映射到值域【Codomain】中同一元素,譬如isEven函数。见左图所示,这对应于抽象【Abstraction】思维过程,这意味 "我真的不在乎我是从哪个点映射来的 ,我只对其中的一处性质【property】感兴趣",所以我在抽象,我抛弃了一些关于它是x_1还是x_2的信息,并只留下一片信息。例如,我不关心被映射的确切数字,只关心它是否是偶数这一个信息,所以在这里,我们对信息做了筛除,但是我们无法对这个过程取逆。
  2. 像集【Image】不必填满整个值域【Codomain】。见右图所示,这对应于建模【Modeling】思维过程,我们将定义域中的这个集合建模到像集【Image】中的集合中 ,因此我们可以说 "我们在第二个集合中识别到了由第一个集合定义的模式【pattern】"。例如,定义域中的简笔画小人是一个住在山洞中的土著,而值域中的简笔画,则是其投在石壁上的影子。

数学家早就对这些概念有了定义,但它们习惯于反着来:

  1. 不会折叠定义域中集合的函数被称为单射【injection】,单射函数中没有收缩【shrinking】,没有遗忘【forgeting】,没有抽象【abstraction】,而只是注入【inject】
  2. 像集填满整个值域的函数被称为满射【surjection】

单射 + 满射 = 同构

注:一般意义上的关系【relation】无需是total的

注:函数无需具有对称性

上面对单射与满射的定义是关于元素的,它们是基于集合论基础的。而在范畴论中,如果你想定义一个 Object 或一个 Arrow 的性质【property】,你基本上是根据其他所有东西来定义它的,这是一种非常全面的方法。因为在范畴论中,我们无法窥探某个 Object 中的元素,在范畴论中,Object 就是一个点,这时,我们手里的显微镜就不起作用了,我们无法像在集合论中那样窥探某个集合内的元素。但是我们的天文望远镜还可以起作用!!  我会看着整个宇宙,然后说 "这个小家伙是在与整个宇宙存在着关系",而它与宇宙的关系的类型,可以是内射的或满射的 。

下面我们用范畴论的方式,来重新定义单射与满射:

单射

  • 满射:给定集合 a、b,以及f::a ->b,对于任意集合 c ,以及任意g1::b -> c、g2::b->c,若 g1。f = g2 。f ,则必然g1 = g2,我们再这里只使用了范畴论术语,未提到任何关于元素的词眼。该定义适应于任何范畴,如果将其应用到集合论中,那么它就等于满射,在其他范畴中,他依然有效,因为我们只使用了组合。需要注意的是,我们在定义中使用了这样的限定词,for-all,我们在定义中,不仅考虑范畴中的所有 Object ,即所有的集合 c ,还考虑了所有的态射序对, 即所有的 b->c 态射g1/g2。我们为了定义函数 f 的一个性质(满射性质),实际上是在观察整个宇宙
  • 单射::给定集合 a、b,以及函数 f::a ->b,对于任意集合 c,以及任意函数 g1::c -> a、g2::c->a,若 f 。g1 = f 。g2 ,则必然g1 = g2,则我们可以说 f 是单射的。

注:为了定义一个性质,我们需要动用整个宇宙,在单射与满射的定义中,我们用到了所有对象 c 和所有的态射序对 g1/g2 ,因此我们定义的是一个全局性质【global property】,或者说是一个泛性质【universe property】,我们还将看到很多在范畴中定义的泛性质【universe property】

现在我们来讨论一下集合【sets】,因为集合【sets】是类型【Types】的模型,现在我们来想想我们所能想到的最简单的类型,或者最简单的集合是什么? 

集合论中最简单的集合是什么? 空集~~那么空集对应于编程中那个类型?在命令式语言中,您不会在任何地方找到空集。但是在haskell中你可以,它对应于 Void 类型。Void 类型不包含任何类型,这意味着它没有构造器,我们没有任何方式可以构造一个Void类型的元素,而对于绝大对数其他类型而言,你总有一种方式可以构造出一个属于该类型的元素。

:在haskell中,类型很有趣,因为每个类型都包含一个bottom元素。因此haskell中空集对应的类型并非是空的,因为它包含bottom元素。

问题一:我们能定义以Void为参数的函数吗?

答案:从数学上讲,它是存在的,你可以把这个函数称为虚张声势【bluff】,譬如 f ::Void -> Int,即使存在这样的函数,但是我对你提出这样对的挑战:你有本事来调用我。只要你提供我一个Void类型的元素,我就可以返回你一个integer类型的值。如果你不能构造一个以Void为参数的函数,那么你自然也不能构造这个非常重要的函数 id :: Void -> Void。我们说过,范畴中任一 Objetc 均存在一个恒等函数【Identity Function】。虽然我们无法调用这些函数,但那是另一回事了,我们这里只关注于它确实存在,它存在于真空中,数学上称某些东西存在真空,是因为无法为其提供参数,或不能实际对其进行测试。  

参数类型为Void的函数,也被称为谬函数【absurd function】。

实际上,absurd这个名字很有意义,如果你开始把编程或类型论转换成逻辑论【logic】,因为在逻辑上讲,Void 对应于False,我们不能从某些事物中构建虚假,你无法证明原本就是false的东西,如果某个事物它是false,他就是false,他不会有证明。这意味着它不会有函数,函数 = 证明!!在类型与逻辑的这个对应关系中,命题对应于类型,而证明,则对应于函数的存在!!!那么命题是什么?在这种情况下,因为你无法创建一个Void,我们无法证明假【Falsity】,假【Falsity】就是假的【false】。另一方面,如果你假设 false,那么从假【false】就会得到任何东西,即以 false作为前提,你仍然可以派生出任何东西,这也是为什么我们有这个谬函数【absurd function absurd:: Void -> a】存在了。

命题【Propositions】类比类型【Types】是一个有深度的概念。它描述了给定逻辑和给定编程语言之间的对应关系。表面上,它说逻辑中的每个命题在编程语言中都有相应的类型——反之亦然。因此我们有:
    propositions as types
更深入一点,对于给定命题的每一个证明,都有相应类型的程序/函数,反之亦然。 因此我们也有 
    simplification of proofs as evaluation of programs
因此,我们不仅有命题【Propositions】与类型【Types】之间的浅显的双射【bijection】关系,而且有一个真正的同构,它保留了证明与程序/函数、化简与求值的深层结构 。

命题【Propositions】类比类型【Types】是一个有深度的概念。 它适用于一系列逻辑,包括命题【propositional】、谓词【predicate】、二阶【second-order】、直觉【intuitionistic】、古典【classical】、模态【modal】和线性【linear】。 它巩固了函数式编程的基础,解释了函数【functions】、记录【records】、变体【variants】、参数多态性【parametric polymorphism】、数据抽象【data abstraction】、连续【continuations】、线性类型【 linear types】和会话类型【session types】等特性。 它启发了自动证明助手【automated proof assistants】和编程语言的灵感,包括Agda、Automath、Coq、Epigram、F# , Haskell, LF, ML,NuPRL, Scala, Singularity,和Trellys。  

接来来我们来讨论单元素集合,在haskell中,它被称为 unit ,它的符号表示只是一个空元组(或者说是一对括号),写作 () 。

unit 只有一个元素,这个元素可以从无到有,也可以凭空而来,你可以说它由于是单例,而变得独一无二。在 haskell 中,unit 的这个单例元素也被写作 () ,因为 haskell 中类型与表达式的命名空间是分开的,它们可以具有相同的名字~~

从逻辑上讲,unit 对应于True,与 Void 形成了对比(Void 对应于 False)~~

我们首先来看这样一个函数, a -> (),我们可以很容易地构造这样的函数,它忽略了它的参数,凭空创造出一个 unit ,因为 units 就是凭空出现的,只有一个 unit 存在。所以我们将该概述也称作 unit ::  a -> ()

我们接下来再看这样一个函数,() -> Int,该函数将 unit 映射为一个Integer,因为该函数必须是纯函数,所以这个函数必须是常数函数,它总是返回相同的Integer。因此我们可以有下面这些函数:

  • one :: () -> Int
  • two :: () -> Int
  • true :: () -> bool
  • false :: () -> bool

对于任意类型,这里有很多类似的函数,对于bool,我们有两个函数,true 和 false,所以一个集合中有多少元素,就能有多少个这样的函数。

重点:这里发生了什么?我现在有一种很狡猾的方式来定义集合中的元素是什么,而不用讨论元素,因为我们这里只讨论函数(态射) 。

所以,我们可以说,我们拥有一个态射家族,其中所有的态射均源自 unit ,如果我们可以在范畴论中定义 unit 的话,那么该函数家族对应于集合元素的选取 。我们再在后面一次又一次地看到这种情况,这些从一个单例集合(后面我们会对单例集合进行推广),到其他任何集合(或者说是Object)函数,是非常重要的函数,它们以一种方式定义一个集合的元素,或者将来会说一个对象的广义元素【generalized element】。所以我们会用讨论射态取代讨论元素 !!!

注:这种对象并非存在于所有范畴,在集合范畴中,它对应于单元素集合

最后将一个双元素集合,bool,它有两个值,true 、false

以上讲述的所有集合类型,空集和单例集合,属于范畴论中的原子,而双元素集合并非是原子的,因为他是可以通过和类型【sum】构建出来的,因此它不具有独立性,但是 Void 和 Unit ,他们形成了其余部分的基础,我们可以在在其之上,派生出其他的更复杂的类型/对象。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值