从开始知道 Haskell,就患上了 Haskell 恐惧症,原因么,就是总能不断看到不理解的东西,且一旦看下去会有更多不懂的内容。渐渐地,就变得有些退怯。
我自己总结,这就是 Haskell 恐惧症。然后也来尝试分析一下这是怎么个情况。
症状
第一阶段,觉得颇有收获,但又不能融会贯通地去使用。因为灵活应用的前提,是理解。还不甚理解,自然没办法尽情玩耍。
第二阶段,觉得力不从心,「怎么有那么多抽象的概念要理解?」,「怎么有那么多 type class 和里面的方法要记?」,「怎么又那么多 GHC 扩展,都是干嘛使的?」。接着,已上任何一类问题下的任意一点,你都能找到若干篇文章,一般都是深入而不浅出,且不论大部分都是英文。
第三阶段,觉得智商崩盘,想用 Haskell 做一个实际的网站。找了几个最流行的库,发现只能亦步亦趋抄代码,随便多找几个不同人写的教学文章, 发现用到的 API 都不一样。然后,所有的类型签名都像是一团迷雾,傻傻分不清。
病因
为什么在学习 Haskell 的过程中,会让我有这样的感受?为什么已经看过 《Real World Haskell》了,确还是在 Haskell 面前打不起精神?
究其原因,首当其冲是语言太灵活,再来是类型太复杂。
语言太灵活
- Point Free 的使用广泛。并不是说 Point Free 的写法不好,它让代码变得非常简洁,但有时确实会让代码第一眼看上去难以捉摸。
- 语言极具扩展性。任何出现在你眼前的东西都可能是某些库的函数,包括各种诡异的运算符,比如
<*>
,<>
,<$>
,>>=
,*>
,=:
。你看到的东西,不仅可能是一个普通函数,也有可能是data/newtype
类型的构造器。 - 多种多样的语言扩展。往往开了一个扩展,就会又多出一小撮语法。然后会慢慢发现,哦~ 原来
$ (a)
和$(a)
就差一个空格,但完全是天南地北的两个东西。对于一个之前只需要开harmony
的程序员来说,突然间多了那么多开关,真是不太适应。每每遇到一个新的扩展开关,就必须调查清楚为什么要加、以及不加之前的世界是怎么样的。必须承认,扩展开关是一种让语言在试错中进步的好方法。但确实对入门选手不太友好。
类型太复杂
- type class 繁多,
Monoid, Functor, Applicative, Monad, Alternative, MonadPlus, MonadReader, MonadWriter, MonadState, MonadThrow, MonadTrans, MonadIO, MonadFix, MonadResource
。你可能需要先逐一看懂这些 type class 都在定义什么样的接口,又是用来满足什么需求的 - 签名太复杂,当你第一次看到这样的签名时
widgetToPageContent :: Eq (Route master) => GWidget sub master () -> GHandler sub master (PageContent (Route master))
,内心肯定是会有点崩溃的。互相嵌套的自定义类型,到处飞的类型变量。如果再加上type family
,那每次想搞清楚一个函数到底是什么类型的,就要花上不少时间。
疗程
所谓 Haskell 恐惧症,其实真正想表达的是,在学习 Haskell 的过程中,从你以为你准备好了,到你真正准备好,差距是非常巨大的。Haskell 绝对不是一个类似你看完 『XX权威指南』或者『XX入门』之后,就能直接写实用作品的的。
我需要让自己做好以下准备:
- 熟悉所有标准 type class,理解使用的场景
- 熟悉所有常用扩展,理解使用的场景
- 转换封装数据的方式,data/newtype, function
- 转换代码抽象的思路,多用 type class,未后续的扩展性做好准备
- 尽量多地去读已有的函数库,避免重复造轮子,减少自己的代码量
唯一的解药 == 读更多的代码
从个人经验上来说,相比其他任何语言,学习 Haskell 需要投入多得多的时间,来阅读优秀的源代码。在其他语言中,你可能学习的只是设计模式。而在 Haskell 里,你是在继续学习这个语言本身。
跨过学习到实践的这道坎,需要付出的努力不是一两天,或者一星期就能达到的。
持续的学习、阅读、输出,才是正道。
我已经开始我的疗程,慢慢积累起来吧!