Text.Parser.Char中的“string”实现

时间:2017-05-19 03:07:53

标签: string parsing haskell monads traversable

首先,只是一些快速背景。我正在阅读 Haskell Programming From First Principles 一书,并进行了以下练习。

  

尝试编写一个解析器来执行string所做的事情,但使用char

我无法理解,所以我检查了the source for the implementation。我正在试图绕过它。这是:

class Parsing m => CharParsing m where
    -- etc.
    string :: CharParsing m => String -> m String
    string s = s <$ try (traverse_ char s) <?> show s

我的问题如下,从大多数到最不具体。

  1. 为什么需要show

  2. 为什么需要s <$traverse char s <?> s不一样吗?换句话说,为什么我们会抛弃遍历的结果?

  3. 遍历发生了什么?我得到了列表遍历的功能,所以我想我对Parser的Applicative / Monad实例感到困惑。在较高的层面上,我得到遍历将char CharParsing m => Char -> m Char应用于字符串s中的每个字符,然后将所有结果收集到{{1}类型的内容中}}。所以类型很有意义,但我不知道后台发生了什么。

  4. 提前致谢!

1 个答案:

答案 0 :(得分:4)

  

1)为什么需要show

因为show字符串(或Text等)会转义特殊字符,这对错误消息有意义:

GHCi> import Text.Parsec -- Simulating your scenario with Parsec.
GHCi> runParser ((\s -> s <$ try (traverse_ char s) <?> s) "foo\nbar") () "" "foo"
Left (line 1, column 4):
unexpected end of input
expecting foo
bar
GHCi> runParser ((\s -> s <$ try (traverse_ char s) <?> show s) "foo\nbar") () "" "foo"
Left (line 1, column 4):
unexpected end of input
expecting "foo\nbar"
  

2)为什么s <$是必要的?遍历char s <?> s的工作方式是否相同?换句话说,为什么我们要抛弃遍历的结果?

解析的结果是不必要的,因为我们事先知道它是s(如果解析成功)。 traverse将根据解析每个角色的结果不必要地重建s。一般来说,如果不需要结果,最好使用traverse_(它只是结合效果,丢弃结果而不尝试重建数据结构)而不是traverse,所以这是可能为什么函数按原样编写。

  

3)遍历发生了什么?

traverse_ char straverse_,而不是traverse,如上所述)是一个解析器。它尝试按顺序解析s中的每个字符,同时丢弃结果,并通过对s中每个字符的解析器进行排序来构建它。提醒traverse_ is just a fold which uses (*>)

可能会有所帮助
-- Slightly paraphrasing the definition in Data.Foldable:
traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
traverse_ f = foldr (\x u -> f x *> u) (pure ())