V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
zuozuo
V2EX  ›  问与答

Lisp vs Haskell 选哪一个入手?

  •  
  •   zuozuo · 2013-05-27 17:07:17 +08:00 · 13236 次点击
    这是一个创建于 4235 天前的主题,其中的信息可能已经有所发展或是发生改变。
    想学习一下涵式编程 Lisp 和 Haskell 选择哪个更适合一些?
    18 条回复    1970-01-01 08:00:00 +08:00
    lidashuang
        1
    lidashuang  
       2013-05-27 19:00:24 +08:00   ❤️ 1
    为啥不两个都看一下
    levn
        2
    levn  
       2013-05-27 19:10:03 +08:00
    看你对括号的忍耐程度
    insraq
        3
    insraq  
       2013-05-27 19:58:49 +08:00
    Scala is more pragmatic, although it's not pure functional
    someFork
        4
    someFork  
       2013-05-27 20:02:32 +08:00
    不要学 Haskell,yinwang 说得清楚了。
    benyur
        5
    benyur  
       2013-05-27 20:12:17 +08:00
    果断lisp
    Golevka
        6
    Golevka  
       2013-05-27 20:17:18 +08:00
    Standard ML
    duhastmich
        7
    duhastmich  
       2013-05-27 20:19:34 +08:00
    lisp 要简单很多, 一般推荐scheme吧

    @someFork 不要学Haskell?愿闻其详
    davepkxxx
        9
    davepkxxx  
       2013-05-27 20:38:33 +08:00
    Haskell 为了函数式而函数式,ML系都有点这个感觉。
    duhastmich
        10
    duhastmich  
       2013-05-27 20:41:03 +08:00   ❤️ 1
    @Perry 博主说了他在扯淡好不好:) haskell 我就懂点毛皮,博主说的效率问题我不清楚,他也没说不要学haskell,lazy evaluation 的概念是我学haskell的重要收获之一
    Perry
        11
    Perry  
       2013-05-27 20:46:34 +08:00   ❤️ 1
    @duhastmich 恩,yinwang在 http://www.yinwang.org/blog-cn/2013/04/17/languages/ 也提到了haskell的启发。
    zuozuo
        12
    zuozuo  
    OP
       2013-05-27 21:57:01 +08:00
    @lidashuang 嗯,说的也是,现在正在看haskell,看这个主要是想学涵式编程,然后打算看一下List主要是因为我之前一直是写ruby的,想看看ruby到底从Lisp里面学了哪些东西。
    zuozuo
        13
    zuozuo  
    OP
       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
        14
    someFork  
       2013-05-28 01:37:28 +08:00   ❤️ 2
    王垠:不再推荐 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
        15
    zuozuo  
    OP
       2013-05-28 01:40:51 +08:00
    @someFork "所以不用盲目的排斥一些语言,把它们都拿来看一下,互相对比,才会知道到底什么是好的。" 这个说得很好,很赞同,学习不同的语言然后互相对比还能加深对各个语言特性的理解。
    momo5269
        16
    momo5269  
       2013-05-28 02:28:02 +08:00
    看你对括号的忍耐程度 +1
    y
        17
    y  
       2013-05-28 04:10:44 +08:00
    @momo5269
    @levn 不能忍受括号的两位,你们的编辑器是高亮加粗括号的么……
    括号淡淡地放在那里就可以了,好的编辑器输入左括号就会出右括号的,按个键就跳出括号。
    momo5269
        18
    momo5269  
       2013-05-28 04:19:57 +08:00
    @y调侃而已 Racket之类的自动完成是做得很好的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1018 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 22:04 · PVG 06:04 · LAX 14:04 · JFK 17:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.