Haskell 趣学指南 M. Lipovaca (英) MnO2 (繁) 开源书屋 (简) 2013 年 10 月 3 日 目录 第一章 简介 1 §1.1 关于这份教学 . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 §1.2 什么是 Haskell? . . . . . . . . . . . . . . . . . . . . . . . . . 2 §1.3 你需要些什么来使用 Haskell 呢? . . . . . . . . . . . . . . . . 4 第二章 从零开始 6 §2.1 准备好了吗? . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 §2.2 初学者的第一个函数 . . . . . . . . . . . . . . . . . . . . . . . 10 §2.3 List 入门 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 §2.4 使用 Range . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 §2.5 List Comprehension . . . . . . . . . . . . . . . . . . . . . . . 20 §2.6 Tuple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 第三章 Types and Typeclasses 27 §3.1 Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 §3.2 Type variables . . . . . . . . . . . . . . . . . . . . . . . . . . 30 §3.3 Typeclasses 入门 . . . . . . . . . . . . . . . . . . . . . . . . . 31 第四章 函数的语法 37 §4.1 模式匹配 (Pattern matching) . . . . . . . . . . . . . . . . . . 37 §4.2 什么是 Guards . . . . . . . . . . . . . . . . . . . . . . . . . . 42 §4.3 关键字 Where . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 §4.4 关键字 Let . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 §4.5 Case expressions . . . . . . . . . . . . . . . . . . . . . . . . . 49 第 ii 页 目录 Haskell 趣学指南 第五章 递回 52 §5.1 你好,递回! . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 §5.2 实现 Maximum . . . . . . . . . . . . . . . . . . . . . . . . . . 53 §5.3 来看几个递回函数 . . . . . . . . . . . . . . . . . . . . . . . . 54 §5.4 “快速” 排序 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 §5.5 用递回来思考 . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 第六章 高阶函数 61 §6.1 Curried functions . . . . . . . . . . . . . . . . . . . . . . . . . 61 §6.2 是时候了,来点高阶函数! . . . . . . . . . . . . . . . . . . . 64 §6.3 map 与 filter . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 §6.4 lambda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 §6.5 关键字 fold . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 §6.6 有 $ 的函数调用 . . . . . . . . . . . . . . . . . . . . . . . . . 80 §6.7 Function composition . . . . . . . . . . . . . . . . . . . . . . 81 第七章 模组 (Modules) 85 §7.1 装载模组 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 §7.2 Data.List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 §7.3 Data.Char . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 §7.4 Data.Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 §7.5 Data.Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 §7.6 建立自己的模组 . . . . . . . . . . . . . . . . . . . . . . . . . . 111 Haskell 趣学指南 目录 第 iii 页 第八章 构造我们自己的 Types 和 Typeclasses 117 §8.1 Algebraic Data Types 入门 . . . . . . . . . . . . . . . . . . . 117 §8.2 Record Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . 121 §8.3 Type parameters . . . . . . . . . . . . . . . . . . . . . . . . . 124 §8.4 Derived instances . . . . . . . . . . . . . . . . . . . . . . . . . 130 §8.5 Type synonyms . . . . . . . . . . . . . . . . . . . . . . . . . . 135 §8.6 Recursive data structures (递回地定义数据结构) . . . . . . . 140 §8.7 Typeclasses 的第二堂课 . . . . . . . . . . . . . . . . . . . . . 145 §8.8 yes-no typeclass . . . . . . . . . . . . . . . . . . . . . . . . . . 150 §8.9 Functor typeclass . . . . . . . . . . . . . . . . . . . . . . . . . 153 §8.10 Kind . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 第九章 输入与输出 163 §9.1 Hello, world! . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 §9.2 文件与字符流 . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 §9.3 命令行引数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 §9.4 乱数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 §9.5 Bytestrings . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211 §9.6 Exceptions (异常) . . . . . . . . . . . . . . . . . . . . . . . . 218 第十章 函数式地思考来解决问题 227 §10.1 运算逆波兰表示法 (Reverse Polish notation form) . . . . . . 227 §10.2 路径规划 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232 第 iv 页 目录 Haskell 趣学指南 第十一章 Functors, Applicative Functors 与 Monoids 243 §11.1 温习 Functors . . . . . . . . . . . . . . . . . . . . . . . . . . 243 §11.2 Applicative functors . . . . . . . . . . . . . . . . . . . . . . . 257 §11.3 关键字 “newtype” . . . . . . . . . . . . . . . . . . . . . . . . 277 11.3.1 Using newtype to make type class instances . . . . . . 280 11.3.2 On newtype laziness . . . . . . . . . . . . . . . . . . . 282 11.3.3 type vs newtype vs data . . . . . . . . . . . . . . . . . 284 §11.4 Monoids . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 11.4.1 Lists are monoids . . . . . . . . . . . . . . . . . . . . . 289 11.4.2 Product and Sum. . . . . . . . . . . . . . . . . . . . . 290 11.4.3 Any and ALL . . . . . . . . . . . . . . . . . . . . . . . 292 11.4.4 The Ordering monoid . . . . . . . . . . . . . . . . . . 293 11.4.5 Maybe the monoid . . . . . . . . . . . . . . . . . . . . 297 11.4.6 Using monoids to fold data structures . . . . . . . . . 299 第十二章 来看看几种 Monad 304 §12.1 动手做做看: Maybe Monad . . . . . . . . . . . . . . . . . . . 306 §12.2 Monad type class . . . . . . . . . . . . . . . . . . . . . . . . 309 §12.3 走钢索 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314 §12.4 do 表示法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 §12.5 List Monad. . . . . . . . . . . . . . . . . . . . . . . . . . . . 327 12.5.1 A knight’s quest . . . . . . . . . . . . . . . . . . . . . 333 §12.6 Monad laws (单子律) . . . . . . . . . . . . . . . . . . . . . . 336 12.6.1 Left identity . . . . . . . . . . . . . . . . . . . . . . . 336 12.6.2 Right identity . . . . . . . . . . . . . . . . . . . . . . . 337 12.6.3 Associativity . . . . . . . . . . . . . . . . . . . . . . . 338 Haskell 趣学指南 目录 第 v 页 第十三章 再来看看更多 Monad 341 §13.1 你所不知道的 Writer Monad . . . . . . . . . . . . . . . . . . 342 13.1.1 Monoids 的好处 . . . . . . . . . . . . . . . . . . . . . 344 13.1.2 The Writer type . . . . . . . . . . . . . . . . . . . . . 346 13.1.3 Using do notation with Writer . . . . . . . . . . . . . 348 13.1.4 Adding logging to programs . . . . . . . . . . . . . . . 350 13.1.5 Inefficient list construction . . . . . . . . . . . . . . . 352 13.1.6 Difference lists . . . . . . . . . . . . . . . . . . . . . . 353 13.1.7 Comparing Performance . . . . . . . . . . . . . . . . . 355 §13.2 Reader Monad . . . . . . . . . . . . . . . . . . . . . . . . . . 356 §13.3 State Monad . . . . . . . . . . . . . . . . . . . . . . . . . . . 359 13.3.1 Stack and Stones . . . . . . . . . . . . . . . . . . . . . 361 13.3.2 The State Monad . . . . . . . . . . . . . . . . . . . . . 362 13.3.3 随机性与 state monad . . . . . . . . . . . . . . . . . . 366 §13.4 Error Monad . . . . . . . . . . . . . . . . . . . . . . . . . . . 367 §13.5 一些实用的 Moandic functions . . . . . . . . . . . . . . . . . 369 13.5.1 liftM . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370 13.5.2 The join function . . . . . . . . . . . . . . . . . . . . . 373 13.5.3 filterM . . . . . . . . . . . . . . . . . . . . . . . . . . . 377 13.5.4 foldM . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 13.5.5 Making a safe RPN calculator. . . . . . . . . . . . . . 380 13.5.6 Composing monadic functions . . . . . . . . . . . . . . 384 §13.6 定义自己的 Monad . . . . . . . . . . . . . . . . . . . . . . . 386 第 vi 页 目录 Haskell 趣学指南 第十四章 Zippers 数据结构 393 §14.1 来走二元树吧! . . . . . . . . . . . . . . . . . . . . . . . . . . 394 §14.2 凡走过必留下痕迹 . . . . . . . . . . . . . . . . . . . . . . . . 397 14.2.1 Going back up . . . . . . . . . . . . . . . . . . . . . . 398 14.2.2 Manipulating trees under focus . . . . . . . . . . . . . 401 14.2.3 I’m going straight to top, oh yeah, up where the air is fresh and clean! . . . . . . . . . . . . . . . . . . . . . . 402 §14.3 来看串行 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403 §14.4 阳春的文件系统 . . . . . . . . . . . . . . . . . . . . . . . . . 405 14.4.1 A zipper for our file system . . . . . . . . . . . . . . . 406 14.4.2 Manipulating our file system . . . . . . . . . . . . . . 409 §14.5 小心每一步 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410 附录 A FAQ 414 §A.1 我能把这份教学放在我的网站吗?我可以更改里面的内容吗? 414 §A.2 推荐其它几个 Haskell 读物? . . . . . . . . . . . . . . . . . . 414 §A.3 我怎么联络到你? . . . . . . . . . . . . . . . . . . . . . . . . 414 §A.4 我想要一些习题! . . . . . . . . . . . . . . . . . . . . . . . . 414 §A.5 关于作者 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414 §A.6 关于简体译者 . . . . . . . . . . . . . . . . . . . . . . . . . . . 415 §A.7 关于繁体译者 . . . . . . . . . . . . . . . . . . . . . . . . . . . 415 附录 B Resource 416 §B.1 Specification . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 §B.2 Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416 §B.3 Lectures & Articles . . . . . . . . . . . . . . . . . . . . . . . 416 Haskell 趣学指南 目录 第 vii 页 §B.4 Forum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417 §B.5 Online Judge . . . . . . . . . . . . . . . . . . . . . . . . . . . 417 §B.6 Books . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418 §B.7 PL Researchers . . . . . . . . . . . . . . . . . . . . . . . . . . 418 §B.8 Interesting Projects . . . . . . . . . . . . . . . . . . . . . . . 418 §B.9 Taiwan Functional Programming User Group . . . . . . . . . 418 第一章 简介 §1.1 关于这份教学 欢迎来到 Haskell 趣学指南!会想看这篇文章表示你对学习 Haskell 有 很大的兴趣。你来对地方了,来让我简单介绍一下这个教学。 撰写这份教学,一方面是让我自己对 Haskell 更熟练,另一方面是希 望能够分享我的学习经验,帮助初学者更快进入状况。网路上已经有无数 Haskell 的教学文件,在我学习的过程中,我并不限于只参考一份来源。我 常常阅读不同的教学文章,他们每个都从不同的角度出发。参考这些资源让 我能将知识化整为零。这份教学是希望提供更多的机会能让你找到你想要得 到的解答。 这份教学主要针对已经有使用命令式程序语言 (imperative program- ming languages) 写程序经验 (C, C++, Java, Python …) 、却未曾接触过函 数式程序语言 (functional programming languages) (Haskell, ML, OCaml …) 的读者。就算没有写程序经验也没关系,会想学 Haskell 的人我相信都是很 聪明的。 若在学习中遇到什么地方不懂的,Freenode IRC 上的 #Haskell 频道 是提问的绝佳去处。那里的人都很友善,有耐心且能体谅初学者。(译注: 第 2 页 第一章 简介 Haskell 趣学指南 Stackoverflow 上的 #haskell tag 也有很多 Haskell 神人们耐心地回答问题, 提供给不习惯用 IRC 的人的另一个选择。) 我经历了不少挫折才学会 Haskell,在初学的时候它看起来是如此奇怪 的语言。但有一天我突然开窍了,之后的学习便如鱼得水。我想要表达的是: 尽管 Haskell 乍看下如此地诡异,但假如你对程序设计十分有兴趣,他非常 值得你学习。学习 Haskell 让你想起你第一次写程序的感觉。非常有趣,而 且强迫你 Think different。 §1.2 什么是 Haskell? Haskell 是一门纯粹函数式程序语言 (purely functional programming language)。在命令式语言中执行操作需要给电脑安排一组命令,随着命令的 执行,状态就会随之发生改变。例如你指派变量 a 的值为 5,而随后做了 其它一些事情之后 a 就可能变成的其它值。有控制流程 (control flow),你 就可以重复执行操作。然而在纯粹函数式程序语言中,你不是像命令式语 言那样命令电脑 “要做什么”,而是通过用函数来描述出问题 “是什么”,如 “阶乘是指从 1 到某个数的乘积”,“一个串行中数字的和” 是指把第一个数 字跟剩余数字的和相加。你用声明函数是什么的形式来写程序。另外,变量 (variable) 一旦被指定,就不可以更改了,你已经说了 a 就是 5,就不能再另 说 a 是别的什么数。(译注:其实用 variable 来表达造成字义的 overloading, 会让人联想到imperativelanguages中variable是代表状态,但在functional languages 中 variable 是相近于数学中使用的 variable。x=5 代表 x 就是 5, 不是说 x 在 5 这个状态。) 所以说,在纯粹函数式程序语言中的函数能做 的唯一事情就是利用引数计算结果,不会产生所谓的 “副作用 (side effect)”