首页IT科技王垠github(王垠:不再推荐 Haskell_IT新闻_博客园)

王垠github(王垠:不再推荐 Haskell_IT新闻_博客园)

时间2025-09-19 14:18:44分类IT科技浏览5216
导读:投递人 itwriter 发布于 2012-09-14 12:49...

投递人 itwriter 发布于 2012-09-14 12:49 评论(0) 有312人阅读

原文链接

[收藏]

« »

在之前的一篇博文里               ,我推荐从函数式语言入手掌握程序语言              。推荐的两种语言是 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                        ”的函数式语言                    。所以现在我觉得             ,其实世界上的语言并没有什么绝对的标准            。在一段时间认为是错的东西   ,可能却是对的        。所以不用盲目的排斥一些语言                       ,把它们都拿来看一下                 ,互相对比,才会知道到底什么是好的                    。

声明:本站所有文章                   ,如无特殊说明或标注                     ,均为本站原创发布                。任何个人或组织   ,在未征得本站同意时               ,禁止复制               、盗用                      、采集      、发布本站内容到任何网站           、书籍等各类媒体平台    。如若本站内容侵犯了原著者的合法权益                      ,可联系我们进行处理                    。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
如何在局域网内共享文件数据(如何在局域网内共享文件)