我该去哪里学习高级的Haskell?

25

在我回答的一个问题的评论中,SO用户sdcwc基本上指出以下代码:

comb 0 = [[]]

comb n =
    let rest = comb (n-1)
    in  map ('0':) rest
     ++ map ('1':) rest

可以被替换为:

comb n = replicateM n "01"

这让我彻底震惊了。

现在我正在寻找一份教授这些高级概念的教程、书籍或 PDF。我不是寻找针对初学者的“什么是单子”教程或在线参考资料,也不是解释 replicateM 类型的内容。我想学习如何以单子的思维方式有效地使用它们,类似于单子“模式”。


19
你可能无法找到一本能够完成这件事的书,这需要时间和对思维方式的重大转变。只需多多使用Haskell编程,参与社区、练习代码高手挑战、应用等式推理、尝试玩乐,最好是在实际项目中应用它。 - luqui
2
这并不是那么高级的概念,总的来说。这只是一个高阶函数和列表单子。理解列表单子,你就能理解这段代码。 - luqui
6个回答

11

考虑像 sequencefilterMliftM2join 这样的函数,思考它们在每个单子中的工作方式:IO[](->) aWriterState。例如,对于 IO 单子,sequence 顺序执行 IO 操作:

 [IO a] -> IO [a]

写下这些方法的签名并尝试使用它们。有些组合很有趣,有些则不太。

filter的例子:

{-# LANGUAGE NoMonomorphismRestriction #-}
import Control.Monad
import Control.Monad.State
import Control.Monad.Reader

a = filterM (\x -> do putStrLn $ "Put " ++ show x ++ "?"
                      fmap (=="Y") getLine)

b = filterM (const [False,True])

c m xs = runState (filterM k xs) 0
              where k x = do a <- get
                             let p = a + x <= m
                             when p $ put (a+x)
                             return p

d = filterM (flip id)

a 使用IO过滤列表——它会询问用户是否保留每个条目。

b 非确定性地过滤列表——每个条目都可能被包含或不包含。因此,您得到幂集。(试试看!)

c 过滤列表并维护状态。在这种情况下,这是贪婪背包——你有一个容量为 m 的背包,并希望从 xs 中插入尽可能多的物品。

d 过滤列表并维护只读状态。这没什么意思。我将过滤函数用作状态,这样就可以得到 flip filter

单个函数 filterM 可以完成所有这些事情。

如果您为其他函数编写类似的代码,您将获得足够的直觉能够注意到其他地方的单子函数。例如,如何获得以下函数?

dup f x = f x x

那么 dup' f x = f x x x 呢?


非常有信息量的回答,谢谢!我认为这需要更多的赞。您能透露一下“dup”和“dup'”的定义吗? - Philip Kamenarsky
3
在Haskell中,类型始终是一个很好的指南。 dup :: (r -> r -> a) -> r -> a。将 r-> 缩短为 m,则可得到 m (m a) -> m a。因此,对于 (->) r 单子,可以使用 Control.Monad.Instances 中提供的 dup = joindup' = join . join。通常来说, join . join :: m (m (m a)) -> m a - sdcvvc

11

这段代码实际上将Haskell的两个相当隐晦的事实结合在一起:

  • 字符串默认情况下是字符列表
  • 列表是不确定性单子的一个实现,当它们组合时会枚举所有可能的路径。

以下代码片段是等效的:

replicateM n "12"

replicateM n ['1', '2']

replicateM n $ do c <- ['1', '2']
                  return c
我认为最后一个版本实际上最清晰地展示了在这种情况下会发生什么:我们在选择“1”和“2”之间有一个选择,并且n个这样的选择被链接在一起。一旦您理解了这一点,您的Haskell理解水平就足够高了。其余部分只是一些很好的混淆。

谢谢,我发现你所绘制的最新版本对我理解非常有帮助。 - undefined

10

就我个人而言(这肯定不是普遍的),对于列表单子很无所谓 - 在您的示例中,我(弱)认为由于存在许多列表函数,解决方案作为单子函数与仅作为列表函数或通过Data.Traversable或Data.Foldable进行列表函数的概括一样好。

对于单子模式,我更多地考虑效果而非特定的组合器 - State模型读取-写入状态,Reader模型只读“状态”,Writer模型只写“状态”,Maybe模型偏离性(因此可以模拟没有错误消息的错误),Either/Error 模型具有有形错误代码或消息的错误。当您掌握效果时 - 您很快就会意识到需要将它们结合起来,因此单子转换器变得重要。

目前还没有关于高级Haskell的书籍,除了《编程之乐》外 - 这是一本收集了一些相当分散的工作的多作者书籍,因此在决定是否需要该书之前值得浏览副本。Richard Bird的新书有许多高级编码示例,但它使用简单,优美的代码来完成这些。它是一本非常好的书,但并不是一本关于高级语言功能的书。

在缺少书籍的情况下,论文是最好的选择,因为它们比博客更精细。最相关的是Haskell Symposium(曾经称为Haskell Workshop),请检查下面的链接以获取会议记录,其中将列出所呈现的论文,然后搜索您发现有趣的任何内容 - 在大多数情况下,作者都会公开发布论文。

http://haskell.org/haskell-symposium/


8
当你超越状态单子及其子集(writer和reader)并开始思考Cont和List时,使用单子的真正疯狂/令人惊叹的事情就出现了,以及它们在回溯、组合和搜索方面的意义。稍后我会提供更多参考资料,但Oleg和Chung-chieh Shan的LogicT论文是一个好的开始:http://okmij.org/ftp/Computation/monads.html EZ Yang在MR 15中介绍了他在三个单子中的冒险:http://themonadreader.files.wordpress.com/2010/01/issue15.pdf 这个问题的答案也可能很有趣:Creative uses of monads

7

请查看TMR13中的Typeclassopedia。没有其他人建议过它,但它帮助我深入思考了Haskell提供的各种类型类。

注意:它正在更新为第二版,但第一版仍然相关且信息丰富。


2
第二版链接:http://www.haskell.org/haskellwiki/Typeclassopedia - sdcvvc

1

对我来说,学习Haskell语言的好资源有两个:http://learnyouahaskell.com/http://www.realworldhaskell.org/blog/。这两个网站都可以在线免费使用。我希望它们能为你提供一些很好的进阶步骤。即使它们没有教授最高级的概念,但它们可能会让你对其中的内容有一个相当不错的了解,并且知道它们可能有什么用处。


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