开始学习Haskell

754

我花了几天时间试图理解Haskell中的函数式编程范式。我通过阅读教程和观看屏幕录像来实现这一目标,但似乎没有什么真正的效果。

在学习各种命令式/OO语言(如C、Java、PHP)时,练习是我走过的良好途径。但由于我不知道Haskell能做什么,而且有许多新概念要使用,所以我不知道从哪里开始。

那么,你是如何学习Haskell的?是什么让你真正“打破了僵局”?另外,有什么好的起步练习想法吗?


14
http://tryhaskell.org/ - Kirk Broadhurst
15个回答

2573
我将按照你在Haskell方面的技能水平来为您排序,从绝对初学者一直到专家。请注意,这个过程可能需要很多个月(甚至几年),因此比较漫长。
绝对初学者
首先,只要足够熟练,Haskell就可以胜任任何事情。它非常快速(仅次于C和C++),可用于模拟、服务器、GUI和Web应用程序等各种项目。
然而,有些问题对于Haskell初学者来说比其他问题更容易编写。数学问题和列表处理程序是很好的候选对象,因为它们只需要最基本的Haskell知识即可编写。
学习Haskell最基础的好资源有Happy Learn Haskell TutorialLearn You a Haskell for Great Good的前6章(或其JupyterLab adaptation)。在阅读这些内容时,最好也使用所学知识解决简单的问题。
另外两个好资源是Haskell Programming from first principlesProgramming in Haskell。它们每章都带有练习题,因此您可以根据最近几页所学的内容解决小而简单的问题。
一个很好的问题列表可以尝试haskell 99 problems page。这些问题从非常基础的开始,随着你继续做会变得更加困难。做很多这样的练习非常有益,因为它们让你练习递归和高阶函数的技能。我建议跳过任何需要随机性的问题,因为在Haskell中这有点困难。如果您想使用QuickCheck测试解决方案,请查看this SO question(见下面的Intermediate)。
完成其中一些后,您可以转而完成一些Project Euler问题。这些问题按照完成人数排序,这是难度的相当好指示。这些问题测试你的逻辑和Haskell比之前的问题更多,但你仍然应该能够完成前几个问题。Haskell在这些问题上的一个巨大优势是整数没有大小限制。要完成其中一些问题,还有必要阅读《学习Haskell》第7章和第8章。 初学者 在那之后,您应该对递归和高阶函数有了相当好的掌握,所以现在是时候开始解决一些更真实的问题了。一个非常好的起点是Real World Haskell(在线书籍,您也可以购买印刷版)。我发现前几章对于从未进行过函数式编程/使用递归的人来说引入了太多的东西。然而,通过完成先前的问题,您应该会发现它非常易于理解。
通过解决该书中的问题,学习如何管理抽象并构建可重用组件是学习Haskell的绝佳方式。这对于习惯于面向对象(oo)编程的人至关重要,因为正常的oo抽象方法(oo类)不出现在Haskell中(Haskell具有类型类,但与oo类非常不同,更像oo接口)。我认为跳过章节不是一个好主意,因为每个章节都介绍了许多新的想法,这些想法在后面的章节中使用。
过一段时间,您将会到达第14章,即可怕的单子(dum dum dummmm)章节。几乎所有学习 Haskell 的人都难以理解单子,因为这个概念太抽象了。我想不出任何其他语言中像函数式编程中的单子那样抽象的概念。单子使许多想法(例如 IO 操作、可能失败的计算、解析等)统一在一个概念下。所以,如果在阅读单子章节后你仍然不太理解它们,不要感到沮丧。我发现阅读许多不同的单子解释很有用;每个解释都会给问题带来新的视角。这里有一个非常好的单子教程列表。我强烈推荐All About Monads,但其他教程也很好。
另外,理解这些概念需要一定的时间。这需要通过使用和时间来实现。我发现有时候睡一觉比任何其他方法都更有帮助!最终,这个概念会恍然大悟,你会惊讶于自己为什么会费力地去理解一个实际上非常简单的概念。当这种情况发生时,你可能会发现 Haskell 是你最喜欢的命令式编程语言 :)
为了确保您完全理解Haskell类型系统,您应该尝试解决20 intermediate haskell exercises。这些练习使用有趣的函数名称,如“furry”和“banana”,如果您还没有掌握一些基本的函数式编程概念,它们可以帮助您更好地理解。用一堆箭头、独角兽、香肠和毛茸茸的香蕉来度过一个美好的夜晚。
中级
一旦您理解了Monad,我认为您已经从初学者Haskell程序员转变为中级Haskeller。那么从这里去哪里呢?如果您还没有从学习monad中学到它们,我首先推荐的是各种类型的monad,例如Reader、Writer和State。同样,Real world Haskell和All about monads都对此进行了很好的覆盖。要完成您的monad培训,学习monad transformers是必须的。它们可以让您将不同类型的monad(如Reader和State monad)组合成一个。起初可能看起来毫无用处,但使用一段时间后,您会想知道没有它们您怎么生活。
现在,如果您愿意,您可以完成真实世界的Haskell书籍。现在跳过章节并不重要,只要您能熟练掌握monad即可。只需选择您感兴趣的内容即可。
有了现在的知识,您应该能够使用cabal上的大多数软件包(至少是记录在案的软件包),以及Haskell附带的大多数库。可以尝试的一些有趣的库列表如下:
  • Parsec:用于解析程序和文本。比使用正则表达式更好。文档非常好,还有一个真实世界的Haskell章节。

  • QuickCheck:非常酷的测试程序。你需要编写一个始终为真的谓词(例如length (reverse lst) == length lst)。然后将谓词传递给QuickCheck,它将生成许多随机值(在这种情况下是列表)并测试该谓词对所有结果是否为真。也可以参考online manual

  • HUnit:Haskell中的单元测试。

  • gtk2hs:Haskell最流行的GUI框架,可让您编写gtk应用程序。

  • happstack:用于Haskell的Web开发框架。不使用数据库,而是使用数据类型存储。文档相当不错(其他流行的框架包括snapyesod)。

此外,还有许多概念(如Monad概念)需要您最终学习。这比第一次学习Monads要容易得多,因为您的大脑已经习惯了处理所涉及的抽象级别。一个非常好的概述,可用于学习这些高级概念以及它们如何相互关联的是Typeclassopedia
  • Applicative:一种像Monad一样的接口,但不那么强大。每个Monad都是Applicative,但反之则不然。这很有用,因为有些类型是Applicative但不是Monad。此外,使用Applicative函数编写的代码通常比使用Monad函数编写的等效代码更具组合性。请参见Functors,Applicative Functors和Monoids来自learn you a haskell指南。

  • FoldableTraversable:抽象了许多列表操作的类型类,以便可以将相同的函数应用于其他容器类型。另请参见haskell wiki explanation

  • Monoid:Monoid是一种具有零(或mempty)值和一个操作(表示为<>)的类型,将两个Monoid连接在一起,使得x <> mempty = mempty <> x = xx <>(y <> z)=(x <> y)<> z。这些被称为标识和结合律。许多类型都是Monoid,例如数字,其中mempty = 0<> = +。这在许多情况下都很有用。

  • Arrows:箭头是表示输入和输出的计算的一种方式。函数是最基本的箭头类型,但有许多其他类型。该库还具有许多非常有用的用于操作箭头的函数 - 即使仅与普通的Haskell函数一起使用,它们也非常有用。

  • Arrays:Haskell中各种可变/不可变数组。

  • ST Monad:允许您编写具有可变状态的代码,而在单子外部保持纯净且运行速度非常快。有关更多详细信息,请参见链接。

  • FRP:函数响应式编程,一种处理事件,触发器,输入和输出(例如gui)的新型实验性编写代码的方式。我对此了解不多。Paul Hudak's talk about yampa是一个好的开始。

有很多新的语言特性你应该看一下。我会列出它们,你可以从Google、Haskell Wikibook、haskellwiki.org网站和GHC文档中找到关于它们的许多信息。

  • 多参数类型类/函数依赖
  • 类型家族
  • 存在量化类型
  • 幻影类型
  • GADTS
  • 其他...

Haskell 的许多内容都基于范畴论,所以你可能想深入了解一下。一个好的起点是计算机科学家的范畴论。如果你不想买这本书,作者相关的文章也非常好。

最后,你需要了解更多有关 Haskell 工具的知识。这些工具包括:

  • ghc(及其所有功能)
  • cabal:Haskell包系统
  • darcs:一个用Haskell编写的分布式版本控制系统,对于Haskell程序非常流行。
  • haddock:一个Haskell自动文档生成器

在学习所有这些新库和概念时,编写一个中等规模的Haskell项目非常有用。它可以是任何东西(例如小游戏、数据分析器、网站、compiler)。在此过程中,您将能够应用许多您正在学习的内容。您会在这个阶段停留很长时间(这就是我所处的阶段)。

专家

到达这个阶段需要几年时间(来自2009年!),但从这里开始,我猜你会开始撰写博士论文、新的ghc扩展,并提出新的抽象概念。

获取帮助

最后,在任何学习阶段,都有多个获取信息的地方。这些是:

  • #haskell irc 频道
  • 邮件列表。这些值得注册,仅仅是为了阅读进行的讨论 - 其中一些非常有趣。
  • 其他在 haskell.org 主页上列出的地方

结论

嗯,这比我预想的还要长...无论如何,我认为成为 Haskell 熟练者是一个非常好的主意。它需要很长时间,但这主要是因为通过这种方式学习了完全新的思考方式。这不像在学习 Java 后学习 Ruby,而更像在学习 C 后学习 Java。此外,我发现由于学习 Haskell,我的面向对象编程技能也得到了提高,因为我看到了许多抽象思想的新方法。


39
箭头太棒了!首先,你让单子塑造了你的大脑,然后你倒立思考余单子,最后同时做这两件事情就会得到箭头 :) Haskell有很多表达能力,也可以通过类型级编程来打开它。 - ephemient
15
Monad更加强大,但组合性更差...很多人在可以使用更简洁的Applicative代码时使用了Monad。大多数是Functor的东西也是Monad,但如果可以使用fmap,就不必使用>>=return,因为如果可以使用后者,代码会更简单。 - Tom Crockett
8
@pelotom,我已经在那个部分添加了类型类百科全书的链接以及更好的理由来使用 Applicative,并删除了 Functor 部分。由于大多数教材(包括 RWH)都强调 Monad,因此很难按正确顺序学习 Monad 和 Applicative 的概念。另一方面,Teach You a Haskell 教程自我最初写下答案以来已经进步了很多(将近两年时间 :O),并在 Monad 之前教授了 Applicative,也许这现在应该成为学习 Haskell 的推荐方式。 - David Miani
7
@tomf:谢谢!一直以来我都很惊讶这篇答案的表现如此出色——我写它已经快五年了,但它仍然非常强大。不过我很快就需要对它进行更新,因为它有点过时了。它没有提到镜头、管道、约束种类、Haskell平台、类型级数等新主题,而它们是自这篇文章写作以来的重要新内容。你说得对,《Real World Haskell》现在不太好了,很长时间没有更新,很多例子都不能编译。不过我很高兴它还是对你有帮助。 - David Miani
2
@hasufell:是的,这个问题已经相当过时了(它是在2009年编写的,自那以后没有太大变化)。RWH非常过时,因为它是在管道/镜头/堆栈出现之前编写的。LYAH确实有其缺点,但当时我觉得它是最好的Haskell简介资源。不幸的是,自2009年以来,Haskell学习资源似乎已经恶化了,因为现在没有像RHW这样的免费资源。learnhaskell github链接推荐的课程还可以,但整个课程似乎除了解析之外没有实际操作。 - David Miani
显示剩余17条评论

185

我的一些同事在使用《Learn You a Haskell for Great Good!》方面有良好的经验。

该教程旨在帮助那些具有命令式编程语言经验,但以前没有编写过函数式语言程序的人。

另外,您还可以在此处查看答案。


30
我支持这个意见。另外,因为它不太明显,在这里提供一个可下载的教程pdf版本的链接:http://learnyouahaskell.com/learnyouahaskell.pdf。网页设计很好,但我也想在地铁上有一份副本。 - Telemachus
8
我开始学这个,但我的看法是你应该直接学《Haskell实战》。这就像从K&R的C语言书籍学习C语言或者从“白痴指南”的方式学习C语言一样,后者虽然试图简化学习过程,但会忽略重要的内容。我认为直接掌握事实比试着以“命令式方式”学习Haskell更好。 - John Smith
7
我非常喜欢这个,我已经花了很多时间在这个和《Real World Haskell》上。在我看来,《Haskell趣学指南》比《Real World Haskell》提供了更深入的见解,尽管它们都是很好的资源。 - Charlie Flowers
7
我认为先阅读《LYAH》然后再看《RWH》是最佳选择。《LYAH》不仅教你 Haskell,而且教你函数式编程。当你解决问题时,你开始用函数式的方式思考。显然,仅仅读《LYAH》是不足以写一个大型应用程序的,但它可以使你的思维方式朝着正确的方向发展。如果你来自命令式编程背景,这是我认为最好的方法。 - Abdulsattar Mohammed
4
请注意,@Telemachus提到的PDF不是最终版,至少缺少最后一章。 - sdcvvc
显示剩余2条评论

104

这里有一本不错的书可以在线阅读:Real World Haskell

我所做过的大多数Haskell程序都是为了解决Project Euler问题。

我不久前读到的一条建议是:你应该拥有一套标准的简单问题,你知道如何在理论上解决这些问题,然后每当你尝试学习一种新的语言时,你就用该语言实现这些问题。


3
我认为《Real World Haskell》很不错,但到了第五章以后我就不推荐了。 - MasterMastic
为什么选择 @MasterMastic?第五章之后的问题是什么?在我花钱之前,我想知道答案。 - Jay Blanchard
1
@JayBlanchard 在第5章中,您开始获得一个具体的库示例,这很好,但他们告诉您他们要做什么,然后去做了,但他们并没有完全解释为什么,而且也不是非常清楚,还有相当多的魔法十六进制字面量。你只是在按部就班地进行。对我来说,最大的问题不是那个,最大的问题是这本书严重依赖于那些种类的艰难和漫长的例子(足够长,以至于需要超过整个章节)。你几乎无法只阅读您想要的部分。我认为这是一位伟大的作者,拥有惊人的知识,但执行力极其差劲。 - MasterMastic

73

71

在其他人的答案基础上,还有一个非常有用的工具可以帮助你编写代码(例如解决Project Euler问题):Hoogle。你可以使用命令行界面或Web界面

命令行

在安装了Haskell平台后,请确保运行cabal install hoogle安装Hoogle。

Hoogle使用示例:

假设你有一个函数f x = 3 * x + 1,你想将它应用到(5 :: Int)上,然后再将其应用到结果上,以此类推,并获得这些值的无限列表。你怀疑可能已经存在一个函数来帮助你(虽然不是专门针对你的f)。

如果这个函数接受f 5,那么它的类型将为(a -> a) -> a -> [a];如果接受5 f,那么它的类型将为a -> (a -> a) -> [a] (我们假设该函数适用于通用类型而不仅仅是Int)。

$ hoogle "a -> (a -> a) -> [a]"
Prelude iterate :: (a -> a) -> a -> [a]

是的,你需要的函数已经存在了,它叫做iterate。你可以通过使用iterate func 5来调用它!

Web界面

相同示例的结果可以在这里找到。


一旦你了解如何向Hoogle查询所需内容,查找你需要的标准库函数就变得更加容易。 - ankh-morpork

60

Graham Hutton的《Haskell编程》简洁、相当全面,他多年来教授Haskell的经验确实得以展现。无论你以后去哪里,这几乎总是我推荐给人们开始学习Haskell的东西。

特别是第8章("函数解析器")提供了开始处理monad所需的真正基础,我认为它远远是最好的起点,其次是All About Monads。(关于该章节,请注意网站上的勘误:您不能使用do形式而不进行一些特殊的帮助。您可能需要先了解类型类并自行解决此问题。)

对于Haskell初学者来说,这很少被强调,但在相当早的时候学习不仅使用monad,而且构建自己的monad也很值得。这并不难,并且自定义的monad可能会使许多任务变得更加简单。


5
这是一本完全被忽视的书籍(和答案)。关于函数解析器的章节,以及接下来的IO章节,即使没有提到monad,也非常出色,作为一种优雅的教学方法。 - okonomichiyaki

56
不要试图阅读所有带有有趣比喻的单子教程。它们只会让你更加困惑。

5
同意!请查看“抽象、直觉和“单子教程谬论””:http://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/ - ShreevatsaR
我也同意!不过,如果你想尝试一下,这个视频是一个非常好的第一步。它介绍了关于Java编程中的单子,通常会让人们感到更加舒适。 - Carlos Bazilio

31

我建议加入#haskell irc频道并在那里提问。这就是我学习Haskell的方式。如果您像上面建议的那样通过《Real World Haskell》进行学习,在这个频道里得到实时问题解答将会非常有帮助。许多聪明的人们在 #haskell 中为了兴趣和利润而编写Haskell,所以你会得到很多好的建议。试试吧!


5
为了明确:不要仅仅通过IRC频道来学习。也就是说,不要进入频道并问:“我怎样编写Haskell程序?如何进行加法运算?” - alternative
除了 IRC Freenode,最近在 Discord 聊天中也有越来越多关于 Haskell 的热烈讨论。 - daparic

23

19

我还可以推荐Yet Another Haskell Tutorial作为入门教程。

另一个非常好的学习资源(可能更适合中级水平),它对我帮助很大,而且在其他答案中似乎没有提到,是Brent Yorgey的Typeclassopedia,它可以在The Monad Reader(第13期)中找到。

它以非常易懂的方式编写,并包含了以下入门建议(除了其他许多内容):

成为专业Haskell开发者智慧的关键有两个:

  1. 理解类型。

  2. 通过熟悉许多示例,深刻理解每个类型类及其与其他类型类的关系。

The Monad Reader本身就是函数式程序员(不仅仅是Haskell程序员)的绝佳宝藏。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接