Lisp vs Haskell 选哪一个入手?

2013-05-27 17:07:17 +08:00
 zuozuo
想学习一下涵式编程 Lisp 和 Haskell 选择哪个更适合一些?
13014 次点击
所在节点    问与答
18 条回复
lidashuang
2013-05-27 19:00:24 +08:00
为啥不两个都看一下
levn
2013-05-27 19:10:03 +08:00
看你对括号的忍耐程度
insraq
2013-05-27 19:58:49 +08:00
Scala is more pragmatic, although it's not pure functional
someFork
2013-05-27 20:02:32 +08:00
不要学 Haskell,yinwang 说得清楚了。
benyur
2013-05-27 20:12:17 +08:00
果断lisp
Golevka
2013-05-27 20:17:18 +08:00
Standard ML
duhastmich
2013-05-27 20:19:34 +08:00
lisp 要简单很多, 一般推荐scheme吧

@someFork 不要学Haskell?愿闻其详
Perry
2013-05-27 20:20:59 +08:00
davepkxxx
2013-05-27 20:38:33 +08:00
Haskell 为了函数式而函数式,ML系都有点这个感觉。
duhastmich
2013-05-27 20:41:03 +08:00
@Perry 博主说了他在扯淡好不好:) haskell 我就懂点毛皮,博主说的效率问题我不清楚,他也没说不要学haskell,lazy evaluation 的概念是我学haskell的重要收获之一
Perry
2013-05-27 20:46:34 +08:00
@duhastmich 恩,yinwang在 http://www.yinwang.org/blog-cn/2013/04/17/languages/ 也提到了haskell的启发。
zuozuo
2013-05-27 21:57:01 +08:00
@lidashuang 嗯,说的也是,现在正在看haskell,看这个主要是想学涵式编程,然后打算看一下List主要是因为我之前一直是写ruby的,想看看ruby到底从Lisp里面学了哪些东西。
zuozuo
2013-05-27 22:03:34 +08:00
@duhastmich 嗯,lazy evaluation 确实是一个很有启发性的概念,在 ruby2.0里面也加入了lazy的特性:
[1,2,3].lazy.cycle.take(7).to_a #=> [1, 2, 3, 1, 2, 3, 1]
someFork
2013-05-28 01:37:28 +08:00
王垠:不再推荐 Haskell


在之前的一篇博文里,我推荐从函数式语言入手掌握程序语言。推荐的两种语言是 Scheme 和 Haskell。可是出于多种原因,我必须告诉大家,我已经不再推荐 Haskell。这里的原因比较深入,可能不容易说清楚,所以只简述一下。如果有异议的话,可以来信跟我讨论,这样也可以帮我理清思路。

  先说说之前推荐 Haskell 的原因吧。推荐它其实是因为是它的类型关系较 Scheme 清晰,并且有模式匹配等方便的功能。可是类型系统和模式匹配,却不是 Haskell 所专有的。其它的一些语言,比如 OCaml 和 Racket 也有很方便的模式匹配和能力相近的类型系统。

  现在停止推荐 Haskell,其实是出于很多原因的积累:

  1. 类型系统过于复杂

  最开头的时候,Haskell 使用的是普通的 Hindley-Milner 类型系统(HM 系统)。使用这种类型系统的原因是因为程序员不需要写任何类型标记(typeannotation)就可以“静态”的确保类型的正确。可是这样做的代价是,这个类型系统表达能力太弱。很多程序需要拐弯抹角的绕过这个类型系统的种种限制才写得出来。比如,Haskell 的 sum type 导致 constructor 的非常麻烦的多重嵌套,这我已经在一篇英文博文里面比较隐晦的批评了一下。显然 HM 系统灵活性太差,所以 Haskell 内部后来引进了 SystemFw。可是这些系统发展了好多年,还是不能解决问题。到现在,你仍然会在 Haskell 里面遇到莫名其妙的限制。你觉得程序应该编译通过,可是它就是编译不过(比如我这篇英文博客所述)。究其原因,其实是类型系统有问题,而不是程序员的思路有问题。

  有的 Haskell 程序员可能会反驳,说是因为我不能理解 Haskell 的类型系统。那么我可以告诉你,我不但实现了 Haskell 和 ML 所用的 HM 系统,而且实现了比 HM 还要强大的 MLF, intersection type 等类型系统。Haskell 推导不出来的类型,我的系统可以推导出来。所以我说的话其实是出自第一手的依据。

  2. 参数和返回值的类型标记很有必要

  与 Haskell 同门的 SML 和 OCaml 的类型系统也有类似的问题,甚至更加严重(比如 ML 有 value restriction,导致不必要的约束和困惑)。但是很多“常规语言”,特别是像 Java,C++ 等需要类型标记的语言,却没有这个问题。很多人喜欢 Haskell 都是因为用它可以“不写类型标记”,可是现在呢,最好的 Haskell 程序员都是先把类型写下来,才开始写函数。一来这样思路清晰,你知道这函数要处理哪些类型的数据,你就明确的把它写下来,以后再来看,或者给其他人看,都一目了然。二来是因为 Haskell 的类型系统由于加入的一些“不可判定”(undecidable)的扩展功能,有时候已经无法推导出类型了。而给函数的参数和返回值加上类型标记之后,就可以轻松推导出类型。所以你看到,给参数和返回值加上类型标记,不管是对人还是对机器,都有好处。所以经过我一学期的研究得出的结论是,HM 系统的类型推导,其实是多此一举。

  不过需要注意的是,函数的局部变量,其实是不需要类型标记的。比如在 Java 程序里常见的:

  List<String> ls = newArrayList<String>();

  这样的赋值语句,其实是没必要在左边加一个类型标记的,因为右边的类型我们知道。在这一点上C++11 的 "auto" 是一个正确的方向。比如在 C++11 里,你可以写:

  auto ls =newArrayList<String>();

  这种类型推导不难做,基本就是一个抽象解释器。我给 Python 做的 PySonar 类型推导系统里面就实现了这样的功能。

  对任何语言,具体是哪些地方有必要加上类型标记呢?其实有一个很简单的方法来判断:观察信息进出函数的“接口”,把这些接口都做上标记。直观一点说,函数就像是一个电路模块,只要我们知道输入和输出是什么,那么中间的导线里面是什么,我们其实都可以推出来。类型推导的过程,就像是模拟这个电路的运行。这里函数的输入就是参数,输出就是返回值,所以基本上把这两者加上类型标记,里面的局部变量的类型都可以推出来。另外需要注意的是,如果函数使用了全局变量,那么全局变量就是函数的一个“隐性”的输入,所以如果程序有全局变量,都需要加上类型标记。

  3. “纯函数式”并不是好主意

  我最近常常跟同学开玩笑,说“纯函数式”语言是什么意思。“纯函数式”语言是用来描述这样一个世界的,在这个世界里,所有的东西都是“有线”的(wired)。不存在 3G,4G,不存在 wifi,收音机,卫星电视…… 所谓的 monads,其实就是这个布满电缆的世界里的“接线盒”。

  Haskell 编程之麻烦,就是因为这些电缆。你必须小心翼翼的把它们接在一起,安排好,否则就会有各种问题,甚至绊到脚。连生成随机数这么简单的事情,你都得学会使用各种各样的“随机数 monads”。这是因为我们需要记录随机数发生器的“状态”,所以随机数 monad 输入一个随机数发生器,返回一个随机数以及一个新的随机数发生器!想一想,在 C 语言里面,你只需要一个全局变量或者函数内部的 static 变量来记录随机数发生器的状态。到底是谁简单,谁复杂?我想你可能已经意识到,全局变量其实就是 wifi!

  Haskell 的支持者常说,纯函数的语言容易“推理”,容易确保程序的正确。因为它的程序就像“数学的函数”,给同一个输入,就会得到同一个输出。这叫做“referentialtransparency”。可是这种性质,真的可以让程序容易“推理”吗?如果 Haskell 的函数使用了 monads,比如“状态”(statemonad),那么这个函数的“输入”,就几乎永远不会相同。因为那个状态每次都可能变化,所以你实际上还是没法知道那里面是什么!

  记住这一点:世界上没有包治百病的神药。

  4. 惰性求值(lazyevaluation)不是好主意

  关于惰性求值,我基本同意 Robert Harper 的观点。惰性求值让类型变得混乱,让程序的时间和空间复杂度难以分析,而且跟并行计算的原则有根本性的矛盾。而惰性求值的功能,却不是经常有用的。即使需要,在普通的语言里也可以通过 thunk 来实现。所以,惰性求值带来的问题恐怕比它解决的问题还要多。

  很多自称“从 Haskell 衍生”的语言,很多其实都只是有其形,而无其实。一个例子就是 Bluespec,一种硬件描述语言。它虽然自称是从 Haskell 演变来的,看起来像 Haskell,但是它却不是惰性的,类型系统也很简单,所以基本上它已经不是 Haskell。打着 Haskell 的旗号,恐怕是想借助 Haskell 的名声来抬高自己,或者是因为 Bluespec 的创造者 Lennart Augustsson 最早的时候是 Haskell 的主要发起人之一。

  5. 思想局限

  所以综上所述,Haskell 自称的“特性”几乎被实践一一推翻。可是 Haskell 程序员往往炫耀自己的“函数式编程”水平,其实经常陷入一些很难理解的“设计模式”,无法自拔。鉴于这个原因,我停止向大家推荐 Haskell。

  另外需要申明一下的是,我停止推荐 Haskell 并不是因为我想力推 Scheme。实际上 Scheme 也有自己的问题,但是相对来说,它更加简单易懂,符合学习的需要。另外,以前对 C 和 C++ 的批评也许过于偏激。最近为了在 LLVM 上做一些事情,开始重新理解C++,发现它做的好些事情其实是挺不错的,甚至超过好些最炫的,带有“dependenttype”的函数式语言。所以现在我觉得,其实世界上的语言并没有什么绝对的标准。在一段时间认为是错的东西,可能却是对的。所以不用盲目的排斥一些语言,把它们都拿来看一下,互相对比,才会知道到底什么是好的。
zuozuo
2013-05-28 01:40:51 +08:00
@someFork "所以不用盲目的排斥一些语言,把它们都拿来看一下,互相对比,才会知道到底什么是好的。" 这个说得很好,很赞同,学习不同的语言然后互相对比还能加深对各个语言特性的理解。
momo5269
2013-05-28 02:28:02 +08:00
看你对括号的忍耐程度 +1
y
2013-05-28 04:10:44 +08:00
@momo5269
@levn 不能忍受括号的两位,你们的编辑器是高亮加粗括号的么……
括号淡淡地放在那里就可以了,好的编辑器输入左括号就会出右括号的,按个键就跳出括号。
momo5269
2013-05-28 04:19:57 +08:00
@y调侃而已 Racket之类的自动完成是做得很好的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/70309

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX