<译>声明式编程

原文地址:https://bartoszmilewski.com/2...程序员

这是Bartosz Milewski关于范畴论的博客的第二部分,第一部分已经由 garfileo翻译完成,建议你们先看第一部分。第一部分导言的地址是 写给程序员的范畴论

第二部分的导言

在本书的第一部分我曾说范畴论和编程都与可复合性相关。在编程时,你总会不断地把问题分解到一个你能处理其细节的程度,而后一个个地解决每一个子问题,最后把它们自底向上地从新组合起来。大体来讲,这有两种实现的方法:告诉计算机要作什么(what to do),或者告诉它如何去作(how to do it)。也就分别是声明式(编程)和命令式(编程)。编程

你甚至能够从最底层的地方来理解这件事。复合自己能够被声明式地定义,例如hfg的一个复合:segmentfault

h = g . f

或者命令式地定义,这时,先调用f,保留计算结果,再对该结果调用g编程语言

h x = let y = f x
      in g y

一个程序的这种命令式版本一般被描述成按照时间顺序执行的一个指令序列。特别是,对g的调用不能发生在f执行完成以前。至少这是一种概念上的想象————不过,在一个参数传递方式为按需调用的惰性语言中,实际的执行顺序可能彻底不一样。ide

实际上,声明式代码和命令式代码的执行过程只会有一点差别,甚至没有,这取决于编译器有多聪明。但这两种方法论毕竟不一样,尤为在咱们寻找问题的解决方案时和考虑代码的可维护性和可调试性时,它们会彻底不一样。函数

这里有一个重大问题:当面对一个具体问题时,咱们是否老是能够在声明式和命令式的方法中选择一个?进一步,若是有一个声明式的的解决方案,是否必定能够转化为计算机代码?这个问题的答案远非显然,而且,若是咱们可以找到这个问题的答案,咱们对宇宙的理解可能就会迎来一场革命。优化

让我来详细说说。物理中有一个相似的二元性,它要么会是一些潜在的深入原则的一个重要部分,要么会告诉咱们一些有关大脑如何工做的事。理查德·费曼曾经提到,这个对应启发了他在量子电动力学领域的工做。spa

大部分的物理定律有两种表达形式。一种使用局部(local)或无穷小(infinitesimal)的思惟。咱们会在一个小邻域内观察系统的状态,而且预测它在下一时刻如何演变。这种想法一般透过一组的微分方程组的形式表达,而且咱们会在一个周期时间里对它们作积分或求和。翻译

让咱们看看这种形式和命令式思惟有多像:咱们经过一系列的“小碎步”达到最终解,而每个碎步取决于前一步的结果。事实上,物理系统的计算机仿真也是循序渐进地把微分方程组重写为差分方程组,而后迭代它们。这也是行星游戏中宇宙飞船的运动方式。在每个时间步长里,飞船的位置经过一个小的增量改变,它就是速度乘以时间间隔。而速度呢,也是如此迭代计算,它的小增量正比于加速度,也就是力除以质量。调试

asteroids game

牛顿运动定律所对应的微分方程组是有明确写法的:

F = m dv/dt
v = dx/dt

一样的方法能够用来处理更复杂的问题,好比用麦克斯韦方程组来描述电磁场的传播,或者甚至是用格点量子色动力学来描述一个质子中的夸克和胶子的行为。

数字计算机的使用促进了这种与时空离散化相结合的局域思惟,史蒂芬·霍夫曼曾试图将整个宇宙的复杂性约减为一个元胞自动机系统,这种思惟在这个伟大尝试中体现到了极致。

另外一种是全局的方法。咱们观察系统的初始状态和终止状态,而后经过最小化某个函数计算出二者之间的轨迹。最简单的例子就是费马的最小时间原理。它声称光线会沿着时间最小的路径传播。特别地,当没有反射物或折射物时,光线会沿着最短的路径传播,也就是一条直线。可是,光在厚实(透明)的材料中会传播的更慢,好比水或玻璃。因此若是你选择的起点在空气中,终点在水里,那么光就会在空气中传播更长一点以得到水中路线的缩减。这个最小时间所对应的路径使得光在空气和水的界面处发生折射,这导出了斯涅尔折射定律:

sin θ_1 / sin θ_2 = v_1 / v_2

其中,v_1是光在空气中的传播速度。v_2是光在水中的传播速度。

和翻译第一部分的 garfileo同样,我也遇到了不能在代码块中写下标的问题,同时思否的LaTeX也不能写行内公式。因而这里采起了和他同样的约定方式: _表示下标, ^表示上标。

snell

全部的经典力学(结论)都能从最小做用量原理推出。该做用量就是拉格朗日量沿路径的积分(对时间),而拉格朗日量则是动能与势能之差(注意是差而不是和,和是总能量)[所以这个所谓的“做用量”是一个关于路径的函数,而路径是一个坐标关于时间的函数,因此做用量是一个泛函,而经典力学下粒子的路径就是这个泛函取最小值的那个路径。想要更具体了解该推导过程,还须要了解变分法的相关内容。译者注]。当你朝一个目标发射一个迫击炮时,炮弹会先上升,这时重力势能变大,从而,对做用量积累负向的贡献。同时炮弹会变慢,在抛物线的顶点达到动能的最小值。而后它会加速以更快地经过接下来的低势能区域。

图片描述

费曼地最大贡献就是意识到了最小做用量原理能够推广到量子力学。再一次,这个问题被表达成了已知初始状态和终止状态的形式。两个状态间的费曼路径积分被用做计算转移几率。

图片描述

咱们的重点是,在描述物理的定律时有一个奇特的难以解释的二元性。咱们既能够用局部的观念,因而万物都按照一个小增量的序列行进;咱们也能够用全局的观念,一旦咱们声明了初始和终止条件,那么这二者中的过程也就肯定了。

全局的手段也能够用在编程中,例如在实现射线跟踪时。咱们声明(观察的)眼睛位置和光源位置,须要作的是指出这二者间全部可能的光路。咱们不须要精确的最小化每条射线的飞行时间,咱们会利用斯涅耳定律和反射定律来达到一样的效果。

局部和全局的手段最大的不一样就是在于它们对于时空的处理上,尤为是时间。局部的手段永远对这里和当下感兴趣,而全局的手段会作一个长程的静态的观察,就好像将来早就注定了,咱们只不过实在分析某个永恒的宇宙的属性罢了。

没有什么比用函数式响应式编程(the Functional Reactive Programming, FRP)来实现用户交互更能说明问题的了。FRP不是针对全部的可能的用户动做写一个又一个的独立的接口,并让全部人都有那些共享的可变状态的权限,而是把外部事件看做一个无限长的列表,而后对这个列表作一系列的操做。观念上来说,这个包含全部将来动做的列表就已经在那儿了,就像程序的输入数据同样可使用。一个数字π的列表和一个伪随机数的列表,又或是一个包含了来自电脑硬件的鼠标位置的列表,在一个程序看来没有任何不一样。不管是哪一种状况,若是你想要得到(列表的)第n项,那你就必须先得到前n-1项。当这个性质用到瞬时事件里时,咱们就称之为因果律

可这和范畴论有什么必然关系呢?我会说明范畴论契合了一种全局的手段而且所以支持了声明式编程。首先,不像微积分,范畴论没有内置的距离或领域的概念,或者时间的概念。咱们有的是抽象的对象和他们之间的抽象的链接。若是你能经过一系列的步骤从A获得B,那么你只用一步也能够。此外,范畴论的主要武器是泛构造,而这自己就是全局手段的象征。咱们已经在实践中见识过它了,好比,在范畴积的定义中。积定义的方式,就是指明它的性质——一种极其声明式的手段。积是一个具备两个投影的对象,而且是最好的那一个——它优化出了某个性质:可以因式化其余(也知足这种条件的)对象的投影的性质。

productranking

注意把它和费马的最小时间原理或最小做用量原理比较。

相反,让咱们把它和更加命令式的传统的笛卡儿积定义做比较。你在描述如何生成一个积的元素时,会先从一个集合中挑一个元素,再从另外一个集合中挑一个。这是生成一个序对的方式,而后也会有一个拆解序对的方式。

几乎在每一种编程语言中,包括函数式语言,例如Haskell,积类型、余积类型和函数类型都是内置的,而不是经过泛构造定义的;即便已经存在一些创造范畴式的编程语言的努力(例如,参见参考文献中Tatsuya Hagino的论文)

不管是否被直接使用,范畴化的定义支撑了已有的编程结构,而且会引导出一些新的结构。更重要的是,范畴论在声明式的层面上提供了一种能够生成计算机程序的元语言。这也鼓励咱们在实现代码以前就可以导出问题的范式。

致谢

感谢Gershom Bazerman检查个人数学和逻辑,和André van Meulebrouck在编辑上的帮助。

参考文献

  1. The Catsters, Products and Coproducts video.

下一篇:极限与余极限