【转】关于语言的思考

之前写了那么多 Haskell 的不好的地方,却没有提到它好的地方,其实我必须承认我从 Haskell 身上学到了非常重要的东西,那就是对于“类型”的思考。虽然 Haskell 的类型系统有过于强烈的约束性,从一种“哲学”的角度(不是数学的角度)来看非常“不自然”,但如果一个程序员从来没学过 Haskell,那么他的脑子里就会缺少一种重要的东西。这种东西很难从除 Haskell,ML,Clean,Coq,Agda 以外的其它语言身上学到。

Haskell 给我的启发

一个没有学过 Haskell 的 Scheme 程序员最容易犯的一个错误就是,把除 #f(Scheme 的逻辑“假”) 以外的任何值都作为 #t(Scheme 的逻辑“真”)。很多人认为这是 Scheme 的一个“特性”,可是殊不知这其实是 Scheme 的极少数缺点之一。如果你了解 Lisp 的历史,就会发现在最早的时候,Lisp 把 nil(空链表)这个值作为“假”来使用,而把 nil 以外的其它值都当成“真”。这带来了逻辑思维的混乱。

Scheme 对 Lisp 的这种混乱做法采取了一定的改进,所以在 Scheme 里面,空链表 ‘() 和逻辑“假”值 #f 被划分开来。这是很显然的事情,一个是链表,一个是 bool,怎么能混为一谈。Lisp 的这个错误影响到了很多其它的语言,比如 C 语言。C 语言把 0 作为“假”,而把不是 0 的值全都作为“真”。所以你就看到有些自作聪明的 C 程序员写出这样的代码:

int i = 0;
...
...
if (i++) { ...}

Scheme 停止把 nil 作为“假”,却仍然把不是 #f 的值全都作为“真”。Scheme 的崇拜者一般都告诉你,这样做的好处是,你可以使用

(or x y z)

这样的表达式,如果其中有一个不是 #f,那么这个表达式会直接返回它实际的值,而不只是 #t。然后你就可以写这样的代码:

(cond
 [(or x y z)
  => (lambda (found)
       (do-something-with found))])

而不是:

(let ([found (first-non-false x y z)])
  (cond
   [(not (eq? found #f))
    (do-something-with found)]))

第一段代码使用了 Scheme 的一个特殊“语法”,=> 后面的 (lambda (found) ...) 会把 (or x y z) 返回的值作为它的参数 found,然后返回函数计算出的结果。第二段代码没有假设任何不是 #f 的值都是“真”,所以它不把 (or x y z) 放进 cond 的条件里,而是首先把它返回的值绑定到 found,然后再把这个值放进 cond 的条件。

这第二段代码比第一段代码多了一个 let,增加了一层缩进,貌似更加复杂了,所以很多人觉得把不是 #f 的值全都作为“真”这一做法是合理的。其实 Scheme 为了达到这个目的,恰好犯了“片面追求短小”的语言设计的小聪明(参考这篇博文)。为了让这种情况变得短小而损失类型的准确,这种代价是非常不值得的。

Haskell 的类型系统就是帮助你严密的思考类似关于类型的问题的。如果你从来没学过 Haskell,你就不会发现这里面其实有个类型错误。可是 Haskell 做得过分了一点,由于对类型推导,一阶逻辑和 category theory 等理论的盲目崇拜,Haskell 里面引入了很多不必要的复杂性。

各种各样的类型推导我设计过不下十个,其中有一些比 Haskell 强大很多。category theory 其实也不是什么特别有用的东西。很多数学家把它叫做“abstract nonsense”,就是说它太“通用”了,以至于相当于什么都没说。我曾经在一个晚上看完了整本的 category theory 教材,发现里面的内容我其实通过自己的动手操作(实现编译器,设计类型系统和静态分析等等),早就明白了。这里面的理论并不能带来对程序语言的简化。恰恰相反,它让程序语言变得复杂。

我对 Haskell 程序员的“天才态度”也感到厌倦,所以我不想再使用 Haskell,然而我的脑子里却留下了它“启发”我的东西。对 Haskell 的理解,让我成为了一个更好的 Scheme 程序员,更好的 Java 程序员,更好的 C++ 程序员,甚至更好的 shell 脚本程序员。我能够在任何语言里再现 Haskell 的编程方式的精髓。然而让我继续用 Haskell ,却就像是让我坐牢一样。本来很简单的事情,到 Haskell 里面就变成一些莫名其妙的新术语。Haskell 的设计者们的论文我大部分都看过,几分钟之内我就知道他们那一套东西怎么变出来的,其实里面很少有新的东西。大部分是因为 Haskell 引入的那些“新概念”(比如 monad)而产生的无须有的问题。世界上有比他们更聪明的人,更简单却更强大的理论。所以不要以为 Haskell 就是世界之巅。

怎么说呢,我觉得每个程序员的生命中都至少应该有几个月在静心学习 Haskell。学会 Haskell 就像吃几天素食一样。每天吃素食显然会缺乏全面的营养,但是每天都吃荤的话,你恐怕就永远意识不到身体里的毒素有多严重。

专攻一门语言的害处

我曾经对人说 C++ 里面其实有一些好东西,但是我没有说的是,C++ 里面的坏东西实在太多了。C++是一门“毒素”很多的语言,就像猪肉一样。

有些人从小写 C++,一辈子都在写 C++,就像每天每顿吃猪肉一样。结果是他们对 C++ 里面的“珍珠”掌握的非常牢靠,以至于出现了一种“脑残”的现象——他们没法再写出逻辑清晰的程序。(这里“珍珠”是一个特殊的术语,它并不含有赞美的意思。请参考这篇博文。)

比如,很多 C++ 程序员很精通 functor 的写法,可是其实 functor 只是由于 C++ 没有 first-class function 而造成的“变通”。C++ 的 functor 永远也不可能像 Scheme 的 lambda 函数一样好用。因为每次需要一个 functor 你都得定义一个新的 class,然后制造这个 class 的对象。如果函数里面有自由变量,那么这些自由变量必须通过构造函数放进 functor 的 field 里面,这样当 functor 内部的“主方法”被调用的时候,它才能知道自由变量的值。所以为此,你又得定义一些 field。麻烦了这么久,你得到的其实不过是 Scheme 程序员用起来就像呼吸空气一样的 lambda。

很多精通 functor 的 C++ 程序员认为会用 functor 就说明自己水平高。殊不知 functor 这东西不但是一个“变通”,而且是从函数式语言里面“学”过来的。在最早的时候,C++ 程序员其实是不知道 functor 这东西的。如果你考一下古就会发现,C++ 诞生于 1983 年,而 Scheme 诞生于 1975 年,Lisp 诞生于 1958 年。C++ 的诞生比 Scheme 整整晚了8年,然而 Scheme 一开始就有 lexical scoping 的 lambda。functor 只不过是对 lambda 的一种绕着弯的模仿。实际上 C++ 后来加进去的一些东西(包括 boost 库),基本上都是东施效颦。

记得2011年11月11日的良辰吉日,C++ 的创造者 Bjarne Stroustrup 在 Indiana 大学做了一个演讲,主题是关于 C++11 的新特性。当时我也在场,主持人 Andrew 是 boost 库的首席设计师之一(他后来有段时间当过我的导师)。他连夸 Stroustrup 会选日子,只遗憾演讲时间没有定在11点。

虽然我对 Stroustrup 的幽默感和谦虚的态度感到敬佩,但我也看出来 C++11 相对于像 Scheme 这样的语言,其实没有什么真正的“新东西”。大部分时候它是在改掉自己的一些坏毛病,然后向其它语言学习一些东西,然后把这些学习的痕迹掩盖起来。可是到最后,它仍然不可能达到其他语言那么原汁原味的效果。然而,由于 C++ 的普及程度高,现成的代码又多,它的地位和重要性还是一时难以动摇的。所以这些“先辈的罪”,我们恐怕要用好几代人的工作才能弥补。

那么 C++ 有什么其他语言没有的好东西呢?其实非常少。我还是有空再讲吧。

多学几种语言

我今天想说其实就是,没有任何一种语言值得你用毕生的精力去“精通”它。“精通”其实代表着“脑残”——你成为了一个高效的机器,而不是一个有自己头脑的人。你必须对每种语言都带有一定的怀疑态度,而不是完全的拥抱它。每个人都应该学习多种语言,这样才不至于让自己的思想受到单一语言的约束,而没法接受新的,更加先进的思想。这就像每个人都应该学会至少一门外语一样,否则你就深陷于自己民族的思维方式。有时候这种民族传统的思想会让你深陷无须有的痛苦却无法自拔。

原文地址:https://www.cnblogs.com/alantu2018/p/8495820.html

时间: 2024-10-22 06:34:07

【转】关于语言的思考的相关文章

Go语言探险思考笔记(1)

最近接触对象存储,国际上鼎鼎有名的Amazon S3还有Google Cloud Service在国内由于防火墙还有机房过远的问题,并不能投入生产使用.国内有名的对象存储,大家众所周知的七牛云,后台就是Go语言实现的.对于第一个敢吃螃蟹的人,我一直很佩服.于是抱着憧憬的心态走进Go语言的大门. 首先,接触一门语言,就从最啰嗦但是最不能缺少的基础语法和顺序变成开始.对于接触这些,我一般是从搭建好自己的编译构建环境开始,首先下载Go语言SDK,国内下载地址:http://golangtc.com/d

Go语言探险思考笔记

最近接触对象存储,国际上鼎鼎有名的Amazon S3还有Google Cloud Service在国内由于防火墙还有机房过远的问题,并不能投入生产使用.国内有名的对象存储,大家众所周知的七牛云,后台就是Go语言实现的.对于第一个敢吃螃蟹的人,我一直很佩服.于是抱着憧憬的心态走进Go语言的大门. 首先,接触一门语言,就从最啰嗦但是最不能缺少的基础语法和顺序变成开始.对于接触这些,我一般是从搭建好自己的编译构建环境开始,首先下载Go语言SDK,国内下载地址:我这里用的是最新的1.7.2,如果你要用I

对JAVA的static深刻理解(结合C语言的思考)

1 public class statictest { 2 3 4 String X = "我是非静态变量"; 5 6 7 static int butterfly =0; 8 static String staticX = "我是静态变量" ; 9 static String staticZ="我也是静态变量"; 10 11 12 statictest(){ 13 System.out.println("我是构造器"); 1

浅析如何学好C语言

今天,我能够自称是一个混IT的人,并能以此谋生,将来大家能一次谋生,都要感谢两个人:克劳德.香农和约翰.冯.诺依曼,是他们发现了所有的数字化信息,不论是一段程序,一封email,一部电影都是用一连串的1和0进行编码的:是他们发现了我们可以利用一个预先编写好的程序控制机器,并使之完成我们期望它完成的动作.建议大家在心里默念三遍他们的名字,以示仰慕.当然,如果让你们带着现在的知识回到他们的那个时代,那么就没有什么图灵奖了.C语言程序设计是我们的专业基础课,但是C语言本身却是一个非常强大的工具,它是到

C++教程 零基础如何学习C语言!

学习一门技术不是所想的很简单的一回事,学习C语言同样也不是一件简单的事!学好C语言,你想在短时间内学好,肯定是不可能的,但是也不需要你花个十年八年才嫩恶搞精通.今天小编就针对0基础的学员推荐给你们基本书籍以及技巧! 一.要读就读好书,否则不如不读 所有初学者面临的第一个问题便是:如何选择教材.好的开始是成功的一半,选择一本优秀的教材是事半功倍的关键因素.不幸的是,学校通常会帮你指定一本很差劲的C语言课本;而幸运的是,你还可以再次选择. 大名鼎鼎的谭浩强教授出了一本<C语言程序设计>,据说发行量

如果是初学C语言请看完 一些成功人士的心得

转自程先的专栏     今天,我能够自称是一个混IT的人,并能以此谋生,将来大家能一次谋生,都要感谢两个人:克劳德.香农和约翰.冯.诺依曼,是他们发现了所有的数字化信息,不论是一段程序,一封email,一部电影都是用一连串的1和0进行编码的:是他们发现了我们可以利用一个预先编写好的程序控制机器,并使之完成我们期望它完成的动作.建议大家在心里默念三遍他们的名字,以示仰慕.当然,如果让你们带着现在的知识回到他们的那个时代,那么就没有什么图灵奖了. C语言程序设计是我们的专业基础课,但是C语言本身却是

怎样学好C语言,一个成功人士的心得!

今天,我能够自称是一个混IT的人,并能以此谋生,将来大家能一次谋生,都要感谢两个人:克劳德.香农和约翰.冯.诺依曼,是他们发现了全部的数字化信息,不论是一段程序,一封email,一部电影都是用一连串的1和0进行编码的:是他们发现了我们能够利用一个预先编写好的程序控制机器,并使之完毕我们期望它完毕的动作.建议大家在心里默念三遍他们的名字,以示敬仰.当然,假设让你们带着如今的知识回到他们的那个时代,那么就没有什么图灵奖了.C语言程序设计是我们的专业基础课,可是C语言本身却是一个非常强大的工具,它是到

[程序设计语言-摘记&amp;注解]-03:控制流

阅读导航 本系列其他文章目录请戳这里. 0.概述 1.表达式求值 1.1赋值(1)-引用和值 1.1赋值(2)-装箱和拆箱 1.1赋值(3)-多路赋值 1.2表达式里的顺序问题&数学的等值关系 1.3短路求值 2.结构化和非结构化的流程 2.1goto的机构化替代品 2.2继续(Continuations) 3.顺序复合(Sequencing) 4.选择 4.1短路条件 4.2case/switch语句 5.迭代 5.1枚举控制器的循环 5.2迭代器 5.3逻辑控制的循环 6.递归 6.1迭代和

chapter 2 - 导言 - 像经济学家一样思考

前言: 每个领域都有自己的语言和思考方式,学习这个领域的相关思考方法比我们在该领域中使用其他的领域的思考方式要来的好,虽然那可能有时给你带来灵感. 经济学的思考方式: 与其他学科一样,经济学家通常这样讨论他们的主题:先提出理论,再收集数据,然后分析数据,以努力证明或否定他们的理论. 科学方法:观察.理论和进一步观察 理论通常与观察密不分可,没有详细的观察思考哪有理论?而通过观察.理论再观察.理论这样的循环,能使你的理论更接近与真相,能够降低你验证该理论所需的成本. 对于收集数据的方法,在经济学的