解析器组合器的类型

时间:2014-08-20 21:12:36

标签: parsing haskell types

如果我有一个解析器a : Parser A和一个解析器b : Parser B,那么我可以将它组合成一个解析器a | b : Parser (Either A B)。当你开始添加更多替代方案并获得类似Either A (Either B C)的类型时,这会有点麻烦。我可以想象将之前的类型扁平化为Alternative A B C之类的东西。是否有我可以执行的标准转换,或者我是否为Alternative A B C ...等类型生成了大量样板文件。

2 个答案:

答案 0 :(得分:8)

关于Either的有趣之处在于您可以将其用作类型级cons运算符。

A `Either` (B `Either` (C `Either` (D `Either` Void))) --> [A,B,C,D]

所以我们需要做的就是明确这一点。您需要ghc-7.8来支持封闭数据系列:

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
-- ...

type family OneOf (as :: [*]) :: * where
  OneOf '[a] = a
  OneOf (a ': as) = Either a (OneOf as)

现在,您可以更简洁地编写类型:

aorborc :: Parser (OneOf '[A, B, C])
aorborc = a | (b | c)

它仍然是Either,因此您仍然可以轻松地与使用Either的所有现有代码进行互操作,这很不错。

答案 1 :(得分:3)

在Haskell中只有一种可能的和类型,并且由于现成的类实例和辅助函数在许多情况下都很有用,但是当你嵌套它时变得相当笨拙。

解析器的最佳方法是创建自己的数据类型,该数据类型反映您正在解析的结构并直接解析为该结构。让我们举一个关于玩具语言的部分玩具示例。

data Statement = TypeDec String Type
                 DataDec String [Constructor]
                 FunctionDec String LambdaExpression

statement :: Parser Statement
statement = TypeDec <$> string "type " *> identifier <*> string " = " *> type
            <|> DataDec <$> string "data " *> identifier <*> string " = " *> many constructor
            <|> FunctionDec <$> identifier <*> string " = " *> lambdaExpression

通过这种方式,您的数据结构和代码都会镜像您正在解析的语法中的作品。这样做的最大好处是您的数据类型安全,清晰,并且可以在解析后立即使用。

(我永远不会记住*><*的固定性,所以我可能就像你需要括号或其他东西一样,但希望你能得到这个想法。)< /子>