在学习了单子之后,学习Haskell的下一步是什么?

33

我逐渐学习 Haskell,甚至感觉掌握了单子的要领。然而,仍有很多更奇特的东西我几乎无法理解,如 Arrows(箭头)、Applicative(应用)等。虽然从我看到的 Haskell 代码中获取了一些片段,但找到一个真正解释它们的教程会很有帮助(似乎有数十个关于单子的教程...但所有的教程好像只是在单子之后完成!)


9
你应该查看Typeclassopedia,它涵盖了Applicative和Arrow(不确定箭头)的许多概念。你可以在Monad Reader期刊第13期中找到它。 - Raoul Supercopter
28
忘记那些高深的东西。写代码吧。只有当你写了足够多的代码后,你才能理解更高深的抽象概念是如何帮助你的。即使在那时,你很可能不需要ArrowsApplicative也能很好地完成工作: GHC有接近10万行代码,我们甚至从来没有使用过单子变换器。 - Simon Marlow
4
@Simon: 我理解你的观点,但是你真的认为试图抑制对更高级抽象的好奇心是个好主意吗?我个人使用Haskell并不是为了完成任务 - 我可以在Java或Ruby中更有效地工作。我使用Haskell是因为它给我提供了新的思考方式。我认为提问者的问题是出于这种精神 - 不是关于如何“过得去”。 - Travis Brown
5
他的观点是人们一听到"Haskell"就想到"学术抽象概念"而非"编程语言"。如果你学习Haskell但从未实际编写过任何程序,这种误解就会持续下去。Haskell是用于编写计算机程序的,而不是用来让朋友相信你比他们更聪明的工具。 - jrockway
7
@Travis,我担心的是,如果我们给人们这样一个印象,即使用这种语言必须学习和理解这些高级概念,那么我们可能会让人们远离这种语言。 - Simon Marlow
显示剩余2条评论
9个回答

55

以下是我在掌握单子后发现有用的一些资源:

  • 正如SuperBloup所指出的,Brent Yorgey的Typeclassopedia是不可或缺的(它实际上也涵盖了箭头)。
  • Real World Haskell中有很多优秀的东西可以被认为是“在单子之后”:例如适用性解析、单子转换器和STM。
  • John Hughes的"Generalizing Monads to Arrows"是一个很棒的资源,它对于单子和箭头的理解都给予了我很大的启示(尽管当我阅读它时认为我已经理解了单子)。
  • "Yampa Arcade"论文是函数式反应编程的一个很好的介绍。
  • 关于类型族:我发现与它们一起工作比阅读关于它们的内容更容易。vector-space包是一个开始的地方,或者您可以查看Oleg Kiselyov和Ken Shan的course on Haskell and natural language semantics中的代码。
  • 选择Chris Okasaki的Purely Functional Data Structures的几章,并仔细研究它们。
  • Raymond Smullyan的To Mock a Mockingbird是一本极其易懂的组合逻辑介绍,会改变你写Haskell的方式。
  • 阅读Gérard Huet的Functional Pearl on zippers。代码使用OCaml编写,但是当您阅读这样的论文时,将OCaml翻译成Haskell在脑海中也非常有用(而且不太困难)。

最重要的是,深入挖掘你发现自己正在使用的任何Hackage库的代码。如果它们在语法、习惯用法或扩展方面做了一些你不理解的事情,请查找相关资料。


4
我希望我能够收藏你的回答。 - sastanin
1
这些都是很好的资源,但我认为Okasaki的书可能是最基础且最有立即用处的。 - John L
这里有很多好链接,谢谢!也感谢其他人的回答 :) - guhou
3
更新的链接:Typeclassopedia关于Zipper的函数珍珠 - kylc
谢谢@kylc!我已经修复了答案中的链接。 - Travis Brown

9
关于类型类:
  • ApplicativeMonad更简单。我最近在其他地方说了一些关于它的事情,但要点是它涉及增强的Functor,可以将函数提升到其中。要了解Applicative,您可以尝试编写使用Parsec而不使用do符号的内容——我的经验是,对于直接的解析器,应用程序样式比单调样式更有效。

  • Arrow是一种非常抽象的处理类似函数(类型之间的“箭头”)的方法。除非你偶然发现自然具有Arrow属性的东西,否则很难理解。曾经我在编写带有反馈循环的交互状态机时重新发明了Control.Arrow的一半(效果不佳)。

  • 虽然您没有提到它,但一个常常被低估的强大类型类是谦逊的Monoid。可以在许多地方找到类似于单调结构的结构。例如,请查看monoids package


除了类型类,我会给你一个非常简单的答案:写程序!通过实践学习是最好的方法,所以选择一些有趣或有用的东西,然后让它发生。
事实上,许多更抽象的概念——比如箭头(Arrow)——可能会在你回过头来再次接触它们时变得更加清晰,就像我一样,它们为你遇到但还没有意识到可以抽象出来的问题提供了简洁的解决方案。
然而,如果你想要一个具体的目标,为什么不看看Functional Reactive Programming呢?这是一系列很有前途的技术,但仍有很多未解之谜,即什么是最佳实践。

Monad回到ApplicativeFunctorMonoid是一个好的选择,我建议按相反的顺序(从Monoid开始)。然后再回到Monad并查看Arrow。我敢打赌,将它放入更广泛的上下文中,你会更好地理解MonadMonad有很多神奇的想法。它只是另一种类型类,即接口加代数定律。 - Conal
1
@Conal:这个答案已经超过一年了,如果有什么我会改变的事情,那就是更强调Monoid。它可能是最简单的结构,真正展示了代数思维的力量,而且很多其他东西都是基于对monoid的变化定义的。我认为我实际上会将CategoryFunctorApplicative并列,尽管我会把Arrow留到后面。 - C. A. McCann
感谢您提醒关于Category的事情。我完全同意您的观点。简单而广泛适用。 - Conal

4

MonadApplicativeArrowFunctor这样的类型类很棒,它们甚至比函数通用性更能改变您对代码的思考方式。但是有一个普遍的误解,即Haskell的“下一步”是学习更多的类型类和控制流程结构方式。下一步是决定您想要编写什么,并尝试编写它,探索您需要的内容。

即使您理解了Monads,这也并不意味着您已经掌握了使用monadically结构化代码的表面。使用解析器组合库进行实验,或者编写自己的解析器组合库。探索为什么适用符号有时更容易。探索为什么将自己限制在应用程序解析器上可能更有效。

看看逻辑或数学问题,并探索实现回溯的方法--深度优先,广度优先等等。探索ListT和LogicT以及ChoiceT之间的区别。看看continuations。

或者做完全不同的事情!


4

迄今为止,你能做的最重要的事情就是探索更多的Hackage。与Haskell的各种奇特特性搏斗可能会让你找到改进某些问题的解决方案,而Hackage上的库将极大地扩展你的工具集。

Haskell生态系统最好的部分在于你可以平衡学习精确新抽象技术和学习如何使用Hackage上可用的巨大工具。


4

开始编写代码。随着你的学习,你会学到必要的概念。

除了语言本身,要有效地使用Haskell,您需要学习一些现实世界中的工具和技术。需要考虑以下几点:

  • Cabal,一个管理依赖关系、构建和部署Haskell应用程序的工具*
  • FFI(Foreign Function Interface),用于从Haskell代码中使用C库**
  • Hackage,作为他人库的来源。
  • 如何进行分析和优化
  • 自动化测试框架(QuickCheck、HUnit)。

*) cabal-init 可以帮助您快速启动。

**) 目前,我最喜欢的 FFI 绑定工具是 bindings-DSL


4

作为下一步单一的步骤(而不是半打“下一步”),我建议您学习编写自己的类型类。以下是一些简单的问题,可供您入门:

  • 为QuickCheck编写一些有趣的实例声明。比如说,您想生成一些在某种程度上“有趣”的随机树。

  • 接下来,请解决以下小问题:定义函数/\\/complement(“与”、“或”和“非”),它们不仅可以应用于布尔值,还可以应用于任意元数的谓词。(如果您仔细查看,可以在SO上找到答案。)


3
你已经掌握了编写代码所需要的一切技能。但是如果你想学习更多与Haskell有关的知识,我建议你:
- 学习类型家族(Type families),这是一个非常方便的功能,它基本上为你提供了一种在类型层面上编写函数的方法。当你试图编写一个参数是多态的函数并希望其具有非常精确的重载时,类型家族会变得十分实用。下面是一个例子: ``` data TTrue = TTrue data FFalse = FFalse class TypeLevelIf tf a b where type If tf a b weirdIfStatement :: tf -> a -> b -> If tf a b
instance TypeLevelIf TTrue a b where type If TTrue a b = a weirdIfStatement TTrue a b = a
instance TypeLevelIf FFalse a b where type If FFalse a b = b weirdIfStatement FFalse a b = a ``` 这样可以得到一个像`if`语句一样运行的函数,但是它可以根据所给的真值返回不同的类型。
- 如果你对类型级编程感到好奇,类型家族提供了一条进入这个主题的途径。
- 学习模板Haskell(Template Haskell),这是一个庞大的主题。它让你拥有类似于C语言中宏定义的强大功能,但是比它更加类型安全。
- 了解一些领先的Haskell库。我数不清有多少次parsec让我能够快速地编写出极其有用的实用程序。 dons定期在hackage上发布受欢迎的库的列表; 请查看这里


0

写一个 Haskell 编译器 :-).


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