最近在群聊的时候,我们聊到了一个观点。

亚里士多德时代的科学不可能容许黑魔法,因为亚式科学的核心是 - 用直觉观察事物,得出物体的本质。黑魔法这种反常识的东西,只可能在可证伪科学体系下出现 - 这不你看,js就是现代产物(

我的一个写C++的朋友趁机黑一把:‘函数式编程也是现代产物’。

我想了想,拿出了收藏已久的The Craft Of Programming

为什么呢?很简单:这本书是本Gateway Drug。

这是第13页。很简单的Imperative程序,跟FP的学院派黑魔法正好形成鲜明对比。

17页开始教你怎么自顶向下写程序,很稳啊,一步一步来。

22页。给出程序的时候还加入了大量assertion,保证出错能debug到问题的那一行,很健壮啊。

这本书有400多页,太长了,我得加快进度。跳到了第44页,好像还行啊,这在说,假设满足P下运行程序S1,会满足Q,满足Q下运行S2,会满足R,满足P下运行S1后接S2会满足R。

直觉上还挺好理解的。。。不过为啥越来越学院派了?

89页。我是在上逻辑学课程吗?

95 97页。函数出来情有可原,为啥出现了交换图?喵喵喵?

159页。be。。beta reduction?

178页。有高阶函数已经算不了啥了。

206页。Environment跟Denotational Semantic都出来了。

到了这一步我们有啥?

program calculation(stepwise refinement)

equivalence law

type derivation

simply typed lambda calculus

environment

denotational semantic

这跟SML还差啥?

HM?(我们可以写generic program,这些program等价于inline,然后因为是inline,所以有equivalence constraint。用这些constraint可以导致不需要写类型,可以去推导)

ADT?(其实这门语言是Algol W,已经有ADT了)

First Class Reference?(且不说Reference怎么算作函数式特性,这本书作者,John Reynold的Essence of Algol里面就很好的引入了Reference。)

Garbage Collection?(试想象下C程序员指着Java程序员说你丫太学院派了)

所以说,你看看,Imperative Programming跟Functional Programming有啥本质区别呢?这些特性,每一个的加入都如此理所当然,但是那一步才算是一个‘函数式编程语言’?

我们早就过了亚里士多德时代,发现了世界并没有这么多‘本质’。无论是那种语言,都是一步步从已有语言摸索出来的,只要你跟着,每次都学一个最小改动,到最后,你也会发现‘就这回事啊’。Lambda Cube是如此,Algol到Prolog是如此Algol到Haskell更是如此

既然编程语言之间并没有本质差别,那,什么是编程的流派?什么是面向对象/函数式/过程式?编译语言/解释语言/机器语言/电路描述语言?

答:流派未月亭。没有打错。没有发疯。

在起初,John Mccarthy没搞懂Lambda Calculus,导致JMC Lisp并不对应Lambda Calculus,连Lexical Scope都没有。Lexical Scope是Algol加入的,而编程语言跟Lambda Calculus的对应是Landin发现的。但这不是跟ISWIM对应,是跟Algol!OOP的元老之一,Luca,做过ML Module的奠基性工作,另一个元老,Alan Kay,借助了FExpr(MACLISP)的概念。Backus写出了Fortran后广播了不朽的’Can Programming be liberated from Von Neumann Style?’。借鉴了OO的Actor跟Algol的Scope的Guy Steel,又写出了Scheme,之后本人更是投身于Java跟Fortress这两门OO语言。而Algol,则受到了JMC吹枕边风,加入了if else expression跟recursion。The craft of programming并不是偶然,而是必然:所有流派都在诞生之初互相纠缠,至今从未分开 - 这不你看,ICFP还会接OO paper,OOPSLA不也一样,更不用说Scala这种融合两种范式的语言(当然,这样的work历史上屡见不鲜。)。

另,各种编程语言之间,只要发展到了一定地步,就可以用库实现各种feature。Scheme有lambda the ultimate imperative/goto,说只需要lambda就能实现assignment/dynamic scope/while/goto,Haskell有lazy functional state thread有typing dynamic typing有final tagless,要状态要动态类型要封装要可扩展性都可以自己随手实现,smalltalk能用object代表conditional,scala能用object代表module。OCaml也实现了effect system,发现go full circle,跟Haskell用monad表示的extensible effect殊途同归。而Haskell中通过first class Typeclass(Constraint,Dict)跟Constraint上定义subtyping typeclass,再加上Existential Type,就能随意控制扩展性 - 我就玩过这样的trick刚好3次。只要语言支持一定的功能,什么范式都能加进去。所以你看,所有主流语言都是多’范式‘的。到了这种地步,何必继续拘泥于这种划分?

编译语言/解释语言等更是虚妄,要知道,一个scheme就可以编译成机器码/有直接吃scheme表达式的机器/还有把scheme 程序弄成电路的尝试。连system verilog都有class了,这等划分的意义可在?

如果我想写得好听点,我可以说,因为绝大部分语言都有多种范式,范式定义永不明确,是由很多更小的,正交的feature一个个组成的,所以我们应该求同存异,抛弃各种范式的偏见,就跟过去跟现在一样继续互相学习。。。但是我不。

因为流派未月亭。

这就是编程的本质,编程的流派,编程的范式。流派未月亭。

就是如此荒谬,毫无意义。

只要我们一天还在用着‘范式’去讨论编程语言,我们就会永远陷入这种情况:

如何通俗易懂地举例说明“面向对象”和“面向过程”有什么区别?

面向对象编程是否是从根本上反模块化且反并行的?为什么?

面向对象编程的弊端是什么?

如何通俗易懂地举例说明“面向对象”和“面向过程”有什么区别?

永远。大家只是互相竖起远离实际的符号,互相实施稻草人謬誤。Ad hoc polymorphism跟module成为OO的专利(而前者在Algol 68就有了,后者pascal的作者也加入了modula里面)。而需要描述OO缺点的时候,则去称之为‘反模块化’。structural programming最初是为了解决complexity产生的,却称为会造成杂乱的代码。而函数式编程?大家都默认了ref,effect跟extensibility solution(final, row polymorphism, tagless)的不存在。。。

为什么我们不能改变一下呢?比如说,谈论subtyping,我们这样做:

子类型(subtyping)是不是错误(ill-defined)的东西?

subtyping和inheritance的区别是什么?

程序语言设计界是否开始认为 Subtyping 是 Anti-pattern?

谈论mutability的时候,我们聊gedanken里面的三种variable设计:

假设声明了struct Point { int x, int y },

0:identifier就是指针,所有Point的x跟y都共享,毫无意义(只有snobol这样做)

1:identifier绑定上指针,每一个Point内的x y都是可变的,无法控制mutability(C,Java)

2:identifier绑定上Value,然后有指针类型/值。如果需要任何的可变性,用Point*,然后update x通过构造新的point。然后通过sharing跟编译手段降低开销。(SML,OCaml,Haskell)

又或者,我们也可以谈论各种effect system(MTL,State,ST,IO,Extensible,跟语言内建的effect system)来讨论mutability。。。而不是,谈论到mutability,就只会’immutability是大势所趋,implicit parallelism大法好’,又或者’immutable不符合计算机基本模型’等myth。

这样有啥坏处呢?为啥我们不这样做?我不知道。也许,这样我们就不能喊些看上去很酷的名字,就跟圣斗士不能喊天马流星拳一样。

唉。