理解attoparsec实现(第2部分)

时间:2014-12-09 08:46:04

标签: haskell attoparsec

我目前正在尝试研究和了解attoparsec库的源代码,但有些细节我自己无法弄清楚。例如,Parser类型的定义:

newtype Parser i a = Parser {
      runParser :: forall r.
                   State i -> Pos -> More
                -> Failure i (State i)   r
                -> Success i (State i) a r
                -> IResult i r
    }

newtype Pos = Pos { fromPos :: Int }
            deriving (Eq, Ord, Show, Num)

data IResult i r =
    Fail i [String] String
  | Partial (i -> IResult i r)
  | Done i r

type Failure i t   r = t -> Pos -> More -> [String] -> String
                       -> IResult i r
type Success i t a r = t -> Pos -> More -> a -> IResult i r

我还不完全理解的是类型参数r的用法。如果我像这样定义runParser的类型签名会有什么不同:

State i -> Pos -> More -> Failure i (State i) a -> Success i (State i) a a -> IResult i a

是否可以帮助我了解forall r.在这种情况下的确切含义以及为什么有必要在runParser的类型签名中使用它?

提前很多!

更新:进一步澄清我的问题:我目前无法理解的是为什么有必要首先引入类型参数r。可以想象,Parser类型也可以这样定义:

newtype Parser i a = Parser {
      runParser ::
                   State i -> Pos -> More
                -> Failure i (State i) a
                -> Success i (State i) a
                -> IResult i a
}

data IResult i a =
    Fail i [String] String
    | Partial (i -> IResult i a)
    | Done i a

type Failure i t a  = t -> Pos -> More -> [String] -> String
                      -> IResult i a
type Success i t a = t -> Pos -> More -> a -> IResult i a

其中根本不使用类型参数r。我的问题是为什么这个定义会“错误”以及它会带来什么问题......

2 个答案:

答案 0 :(得分:2)

attoparsec创建连续传递样式(CPS)解析器 没有forall,我们就无法连锁 解析器在一起。

这是一个大大简化的版本 涉及的类型和bindP的定义 - monadic绑定运算符。 我们已经消除了故障延续和输入源。

{-# LANGUAGE Rank2Types #-}

type IResult r = r
type Success a r = a -> IResult r  -- Success a r == a -> r

newtype Parser a = Parser {
      runParser :: forall r. Success a r
                -> IResult r
    }

bindP :: Parser a -> (a -> Parser b) -> Parser b
bindP m g =
    Parser $ \ks -> runParser m $ \a -> runParser (g a) ks
                                                  -----
                                                  -----

注意Success a r只是函数类型a -> r

如果我们将runParser的定义替换为:

runParser :: Success a a -> IResult a

我们会在上面带下划线的位置收到类型错误。 要理解这一点,可以计算出以下类型:

ks                                      :: Success b b
runParser m $ \a -> runParser (g a) ks  :: IResult b
\a -> runParser (g a) ks                :: Success b b  == b -> b
a :: b

但是从表达式(g a)我们也可以得出结论a的类型为a 这给了我们类型错误。

基本上Parser a可以被认为是一种方法(或计算) 生成arunParser p ks类型值的方法是 获取该值并将其提供给需要a的函数。 对于任何ks,延续函数a -> r可以包含r类型 唯一的要求是其输入类型为a。 通过在定义Success a a时使用runParser,我们限制了runParser a -> arunParser类型函数的适用性。这就是我们的原因 想要将runParser :: Parser a -> (a -> r) -> r 定义为:

{{1}}

这种CPS风格是一种非常不同的解析方法 在Monadic Parsing in Haskell

答案 1 :(得分:0)

forall r部分表示此类型适用于所有r作为结果,而无需在newtype声明的左侧指定。如Haskell wikibook中所述:

  

forall关键字用于将类型变量显式引入范围。

在使用r之前,forall类型变量不在范围内。那个wikibook页面用几个例子很好地解释了一些事情,所以我鼓励你去看看,但文章的关键是forall就像一个类型的交集,所以如果你把类型视为集合元素,Bool = {False, True, ⊥}Int = {minBound..maxBound, ⊥}Char = {minBound..maxBound, ⊥}等(根据需要展开{​​{1}},minBound..maxBound表示未定义,通常称为底部),然后< / p>

是所有类型的交集,即forall a. a ,而

{⊥}

是受forall a. Show a => a 约束的所有类型的交集。在这种情况下,当您有类似

的类型时
Show

然后构造函数data T = forall a. MkT a 具有类型MkT,允许您将任何类型转换为forall a. a -> T,这个技巧允许您拥有异构列表:

T

但是你不能用这种类型做什么,你怎么打开[MkT (), MkT 1, MkT "hello"] :: [T] 用它来做任何事情?您可以在构造函数上进行模式匹配,但是您只需要一个MkT类型的值,而没有关于它的信息。如果你有

a

然后你可以做类似

的事情
data S = forall s. Show s => MkS s

在attoparsec的情况下,它有点类似于-- Pass through instance of Show instance Show S where show (MkS s) = show s map show [MkS "hello", MkS 5, MkS (Just 2)] ["\"hello\"", "5", "Just 2"] monad,它使用ST来阻止你编写非法操作。同样,那篇wikibooks文章提供了一个很好的解释。如果不够,请发表评论,我会看看是否可以澄清。