我们今天使用的大多数编程范例都是在1930年代首先使用lambda演算和Turing机器的思想进行数学研究的,它们是通用计算模型的变体(这些是可以执行通用计算的形式化系统)。 Church-Turing论文表明,lambda微积分和Turing机器在功能上是等效的。 即,我们正在谈论的事实是,可以使用图灵机计算的所有内容也可以使用lambda演算来计算,反之亦然。

常见的误解是图灵机可以计算所有可以计算的东西。 仅在某些情况下,使用Turing机器可以计算出许多类的问题(例如,
stop的
问题 )。 在本文中使用“可计算地”一词时,表示“可通过图灵机计算”。
Lambda演算演示了自上而下将函数应用于计算的方法。 Turing磁带机是一种自下而上的,必不可少的(逐步的)计算方法。
低级编程语言(例如机器代码或汇编器)出现在1940年代,到1950年代末,出现了第一种流行的高级语言,该语言同时实现了功能方法和命令式方法。 因此,Lisp语言的方言仍被广泛使用,其中包括Clojure,Scheme,AutoLisp等。 在五十年代,出现了诸如FORTRAN和COBOL之类的语言。 它们是仍然存在的命令式高级语言的示例。 尽管应该指出的是,C族的语言在大多数领域都取代了COBOL和FORTRAN。
命令式和函数式编程的根源在于计算的形式数学,它们出现在数字计算机之前。 面向对象编程(OOP)后来出现了;它起源于上世纪六十年代和七十年代发生的结构编程革命。
我所知道的第一个对象是伊凡·萨瑟兰(Ivan Sutherland)在他于1961年至1962年之间创建的具有决定意义的应用程序Sketchpad中使用的,他在1963年的
这项工作中
对此进行了描述。 对象是在示波器屏幕上显示的图形字符(也许这是使用图形计算机监视器的历史上的第一次),支持通过动态委托进行继承,Ivan Sutherland在其工作中称其为“大师”。 任何对象都可以成为主对象,该对象的其他实例称为“出现”。 这使Sketchpad系统成为实现原型继承的第一种著名编程语言的所有者。
Simula语言是最早的编程语言,通常称为“面向对象”,其规范于1965年开发。 与Sketchpad一样,Silmula提供了处理对象的功能,但还包括类,基于类的继承,子类和虚拟方法。
虚方法是在类中定义的方法,该类旨在由子类重新定义。 通过使用动态分派来确定应在程序执行期间调用哪个特定方法,虚拟方法允许程序调用在编译代码时可能不存在的方法。 JavaScript具有动态类型,并使用委托链来确定要调用的方法,因此,该语言无需向程序员介绍虚拟方法的概念。 换句话说,JavaScript中的所有方法都在运行时使用分派;因此,不需要将JavaScript中的方法声明为“虚拟”即可支持此功能。OOP父亲关于OOP的意见
“我创造了术语“面向对象”,并且我可以说我的意思不是C ++。 艾伦·凯(Alan Kay),OOPSLA会议,1997年。艾伦·凯(Alan Kay)创造了“面向对象编程”一词,指的是编程语言Smalltalk(1972)。 该语言由艾伦·凯,丹·英格尔斯和施乐PARC研究中心的其他员工开发,是Dynabook设备项目的一部分。 Smalltalk比Simula更面向对象。 在Smalltalk中,一切都是对象,包括类,整数和块(闭包)。 该语言的最初实现为Smalltalk-72,没有子类化的能力。 此功能出现在Smalltalk-76中。
虽然Smalltalk支持类并因此支持子类化,但Smalltalk并未将这些想法放在首位。 Lisp和Simula一样,是一种功能性语言。 根据Alan Kay的说法,将类视为代码重用机制是一个错误。 编程行业非常关注子类的创建,这分散了面向对象编程的真正优势。
JavaScript和Smalltalk有很多共同点。 我想说JavaScript是Smalltalk因误解OOP概念而报复的世界。 这两种语言都支持以下功能:
- 对象
- 一流的函数和闭包。
- 动态类型。
- 后期绑定(在程序执行期间可以替换功能和方法)。
- 没有基于类的继承系统的OOP。
“很遗憾,很久以前我为这个现象想出了“对象”一词,因为它的使用导致了这样一个事实,即许多人对一个不如主要思想重要的思想高度重视。 主要思想是消息传递。” 艾伦·凯在2003年的一封电子邮件中,艾伦·凯(Alan Kay)阐明了他将Smalltalk称为“一种面向对象的语言”时的想法。
“对我而言,OOP仅意味着消息传递,本地存储和保护,隐藏状态以及非常晚的绑定。” 艾伦·凯换句话说,与Alan Kay的想法一致,最重要的OOP要素如下:
值得注意的是,发明了“ OOP”一词并带给大众的Alan Kay并不认为继承和多态性是OOP的最重要组成部分。
OOP的本质
消息传递和封装的组合具有几个重要目的:
- 通过封装状态并将其他对象与状态的局部更改隔离,从而避免对象的共享可变状态。 影响另一个对象状态的唯一方法是通过向他发送消息来要求他(而不是命令他)进行更改。 在本地蜂窝级别上监视状态更改,而其他对象则无法使用该状态。
- 对象彼此分离。 消息的发送者通过消息传递API松散地耦合到接收者。
- 通过后期绑定在程序执行过程中的适应性和抗更改性。 在程序执行过程中适应变更具有许多重要的优势,艾伦·凯(Alan Kay)认为这对OOP非常重要。
表达这些想法的艾伦·凯(Alan Kay)的灵感来源是他的生物学知识以及他对ARPANET(这是Internet的早期版本)的了解。 即,我们正在谈论生物细胞以及连接到网络的单个计算机。 即便如此,艾伦·凯(Alan Kay)仍在想象程序如何在大型分布式计算机(Internet)上运行,而单个计算机充当生物细胞,独立地以其自身的隔离状态工作,并通过发送消息与其他计算机交换数据。
“我意识到,单元或计算机的隐喻将有助于摆脱数据[...]。 艾伦·凯当然,艾伦·凯(Alan Kay)说“帮助摆脱数据”时,意识到了由共享的可变状态引起的问题,以及由数据共享导致的强大连接性。 今天,这些话题已广为流传。 但是在1960年代后期,ARPANET程序员对在开发程序之前为其程序选择数据模型表示形式的需求感到不满。 开发人员希望摆脱这种做法,因为在将自己推进到由数据表示所确定的框架中之前,将来更改某些内容更加困难。
问题在于,在某个时间点所使用的编程语言中,使用不同的方式呈现所需的数据,访问它们的方式,不同的代码和不同的语法。 这里的圣杯将是访问和管理数据的通用方式。 如果程序的所有数据看起来都一样,这将解决开发人员在程序开发和维护方面的许多问题。
艾伦·凯(Alan Kay)试图“摆脱”这个想法,根据某种意义上说,数据和程序是独立的实体。 在List或Smalltalk中,它们不被认为是这样。 数据(值,变量,数据结构等)和软件结构(如函数)之间是没有区别的。 功能是“一等公民”,并且允许程序在执行过程中进行更改。 换句话说,Smalltalk与数据没有特殊的特权关系。
此外,艾伦·凯(Alan Kay)将对象视为代数结构,从而为它们的行为提供了明确的,数学上可证明的保证。
“我的数学背景使我了解到,每个对象可以具有与之关联的几个代数模型,可以有成组的相似模型,并且它们可能非常非常有用。” 艾伦·凯事实证明是这样,这构成了对象(例如诺言和镜头)的基础,而且范畴理论受到了两者的影响。
艾伦·凯(Alan Kay)如何看待对象的代数性质将使对象能够提供形式验证,确定性行为并提高可测试性,因为代数模型从本质上讲是服从方程式形式的多个规则的运算。
用程序员的术语来说,“代数模型”是由函数(操作)创建的抽象,这些函数带有某些规则,并由这些函数必须通过的单元测试(公理,方程式)实施。
数十年来,C系列的大多数面向对象语言(包括C ++,Java,C#等)都已经忘记了这些想法。 但是,这些想法开始了人们在最广泛使用的面向对象语言的最新版本中的回归之旅。
在这种情况下,有人可以说编程世界重新发现了函数式编程的优势,并在面向对象的语言中提供了合理的论据。
像更早的JavaScript和Smalltalk一样,大多数现代的面向对象语言都变得越来越“多范式”。 没有理由在功能编程和OOP之间进行选择。 当我们查看每种方法的历史本质时,它们不仅看起来是兼容的,而且是互补的想法。
按照阿兰·凯的思想,在巴解组织中最重要的是什么?
- 封装。
- 讯息传递
- 动态绑定(程序在执行过程中开发并适应更改的能力)。
在OOP中什么可以忽略不计?
- 类。
- 基于类的继承。
- 与对象,功能或数据的特殊关系。
- 关键字
new
。 - 多态性。
- 静态输入。
- 对类的态度是“类型”。
如果您知道Java或C#,您可能会认为静态类型或多态性是OOP的最重要组成部分,但是Alan Kay倾向于以代数形式处理通用行为模式。 这是用Haskell编写的示例:
fmap :: (a -> b) -> fa -> fb
这是通用
map
函子的签名,该函式可与未定义的类型
a
和
b
,并在函子
a
的上下文中应用从
a
到
b
的函数以创建函子
b
。 “ Functor”是数学术语中的一个词,其含义被简化为“支持显示操作”。 如果您熟悉JavaScript中的
[].map()
方法,那么您已经知道这意味着什么。
以下是一些JavaScript示例:
// isEven = Number => Boolean const isEven = n => n % 2 === 0; const nums = [1, 2, 3, 4, 5, 6]; // map `a => b` `a` ( `this`) // `b` // `a` `Number`, `b` `Boolean` const results = nums.map(isEven); console.log(results); // [false, true, false, true, false, true]
.map()
方法是通用的,从某种意义上说
a
和
b
可以是任何类型,并且此方法可以毫无问题地应对类似情况,因为数组是实现函子代数定律的数据结构。
.map()
的类型无关紧要,因为此方法不会尝试直接使用相应的值。 相反,它使用一个函数,该函数期望并返回从应用程序的角度来看正确的相应类型的值。
// matches = a => Boolean // `a` , const matches = control => input => input === control; const strings = ['foo', 'bar', 'baz']; const results = strings.map(matches('bar')); console.log(results); // [false, true, false]
通用类型之间的关系可能很难像TypeScript这样的语言正确且完整地表达,但是在Haskell中使用的Hindley-Milner类型系统中却非常简单,该系统支持更高的类型(类型类型)。
大多数类型的系统施加了过于严格的限制,以至于无法自由表达动态和功能性思想,例如功能的组合,对象的自由组合,程序执行期间对象的扩展,组合器,镜头的使用等。 换句话说? 静态类型通常使使用构建方法编写软件变得困难。
如果您的类型系统有太多的限制(例如TypeScript或Java),则要实现相同的目标,您必须编写比使用语言更自由的键入方法更复杂的代码。 这并不意味着使用静态类型是一个不幸的主意,也不意味着所有静态类型的实现都具有相同的限制。 例如,使用Haskell类型系统遇到的问题要少得多。
如果您是静态类型的爱好者,并且不受限制,希望您在龙骨下方有7英尺高。 但是,如果您发现此处表达的某些思想由于不容易对通过组合其他函数和复合代数结构而获得的函数进行类型化的事实而难以实施,则应将其归咎于类型系统而不是思想。 驾驶员喜欢框架式SUV为其提供的便利设施,但没有人抱怨它们不会飞行。 为了飞行,您需要一种具有更大自由度的车辆。
如果限制使您的代码更简单-太好了! 但是,如果约束迫使您编写更复杂的代码,则这些约束可能有问题。
什么是“对象”?
随着时间的流逝,“对象”一词已经获得了许多次要含义。 在JavaScript中,我们所谓的“对象”只是复合数据类型,没有任何基于类的编程或Alan Kay的消息传递思想的暗示。
在JavaScript中,这些对象可以支持并且经常支持封装,消息传递,通过方法分离行为,甚至使用子类实现多态(尽管使用委托链而不是基于类型的调度)。
艾伦·凯(Alan Kay)希望摆脱程序与数据之间的差异。 JavaScript在某种程度上通过将对象方法与存储数据的属性放在同一位置来实现此目标。 例如,可以为任何属性分配任何功能。 您可以动态构造对象行为,并在程序执行期间更改对象的语义内容。
对象只是一个复合数据结构,不需要任何特殊的东西就可以将其视为对象。 但是,使用对象进行编程不会导致这样的代码变成“面向对象”的事实,就像使用函数不会使代码“起作用”一样。
OOP不再是真正的OOP
由于现代编程语言中的“对象”概念比Alan Kay的含义要少得多,因此我用“组件”一词代替“对象”来描述此OOP的规则。 许多对象直接由某些第三方JavaScript代码拥有和控制,但是组件必须封装自己的状态并对其进行控制。
这是真正的OOP:
- 使用组件进行编程(Alan Kay称它们为“对象”)。
- 组件的状态必须封装。
- 对于实体之间的通信,使用消息传递。
- 可以在运行时添加,修改和替换组件。
可以使用代数数据结构以通用方式定义大多数对象行为。 不需要继承。 组件可以重用公共功能和导入模块中的行为,而不必将其数据公开。
使用JavaScript操纵对象或使用基于类的继承并不意味着有人参与OOP编程。 但是以这种方式使用组件-意味着。 但是,要摆脱有关术语的既定观念是非常困难的,因此也许我们应该保留术语“ OOP”,而将上述“组件”称为“面向消息的编程(MOP)”? 我们将在下面使用术语“ MOP”来谈论面向消息的编程。
偶然地,英文单词“ mop”被翻译为“ mop”,众所周知,它们被用来恢复秩序。
一个好的拖把是什么样的?
大多数现代程序都有一个负责与用户交互的用户界面(User Interface,UI),一些管理应用程序状态的代码(用户数据)以及与系统一起工作或负责与网络交换数据的代码。
为了支持这些系统中的每个系统的运行,可能需要长期存在的进程,例如事件侦听器。 在这里,您将需要应用程序的状态-存储诸如有关网络连接,有关接口控件的事务状态以及有关应用程序本身的信息。
一个好的MOP意味着,不是所有这样的系统都可以访问彼此的状态并能够直接控制它们,而是它们通过消息彼此交互。 当用户单击“保存”按钮时,可以发送
"SAVE"
消息。 状态管理应用程序组件可以解释此消息并将其重定向到负责从状态进行更新的处理程序(例如纯reducer函数)。 也许在更新状态之后,负责状态管理的组件将消息
"STATE_UPDATED"
分派
"STATE_UPDATED"
用户界面组件,该用户界面组件依次解释状态,决定需要更新接口的哪些部分,并将更新后的状态传递给负责与之交互的子组件。特定的界面元素。
同时,负责网络连接的组件可以监视用户与网络上另一台计算机的连接,侦听消息并调度状态的更新视图,以将其保存在远程计算机上。 这样的组件负责使用网络机制,了解连接是否有效等等。
相似的应用程序系统不应该知道其他部分的详细信息。 他们应该只关心解决自己的问题。 系统组件可以作为构造函数进行分解和组装。 它们实现标准化的接口,这意味着它们可以彼此交互。 只要满足组件接口的众所周知的要求,这些组件就可以用具有相同接口的其他组件替换,但是做不同的相同事情,或者执行,接收相同的消息,则完全不同。 您甚至可以在程序执行期间将一个组件更改为另一个组件-这不会中断其工作。
软件系统的组件甚至不必在同一台计算机上。 该系统可以分散。 网络存储可以将数据放置在像
IPFS这样的分散存储系统中,结果,用户独立于特定计算机的运行状况,从而确保了数据的安全性。 通过这种方法,可以可靠地存储数据并保护其不受入侵者的侵害。
PLO部分受ARPANET构想的影响,该项目的目标之一是创建一个分散的网络,该网络可以抵抗核打击等攻击。
良好的MOP系统的特点是,使用支持在应用程序运行时进行热插拔的组件,具有相似的稳定性。 如果用户通过手机使用它并且由于进入隧道而不在网络覆盖范围之内,它将能够继续运行。 如果飓风中断了其服务器所在的数据中心之一的电源,它也将继续运行。
现在是时候让软件世界摆脱不成功的基于类的继承实验,并采用OOP最前沿的数学和科学原理了。
现在是时候让我们的开发人员使用MOP和功能编程的和谐组合来创建更灵活,稳定,美观的程序了。
顺便说一句,缩写词“ MOP”已经在使用,描述了“面向监视的编程”,但是与OOP不同,该概念将简单地消失。
因此,如果“ MOP”一词看起来不像程序员的专业术语,不要气disc。 只需按照上述MOP原则整理OOP。
