【问题标题】:How do I prevent a recursively defined optparse-applicative Parser from hanging?如何防止递归定义的 optparse-applicative 解析器挂起?
【发布时间】:2021-08-18 22:25:15
【问题描述】:

假设我有以下递归定义的 ADT

data GraphType = Character | Colour | Nested GraphType deriving (Show)

我可以为这个结构定义一个解析器(使用optparse-applicative,导入为OA),递归方式如下:

typeParser :: OA.Parser GraphType
typeParser =
  OA.flag' Colour (OA.long "colour")
    <|> OA.flag' Character (OA.long "character")
    <|> (OA.flag' Nested (OA.long "nested") <*> typeParser)

这让我可以传递参数,例如

  • --colour 获取 Colour 的值
  • --nested --colour 获得 Nested Colour 的值
  • --nested --colour 获得 Nested (Nested Colour) 的值
  • 等等

不幸的是,如果我尝试为解析器生成帮助文本,它会失败。 这在一定程度上是有道理的,因为解析器的“结构”是无限大的。 但是,我很乐观地认为可能会有某种解决方法,例如转换内部 typeParser,这样我们就不会尝试为其生成帮助文本。

可以对此解析器进行的最小修改以使其保留有效的帮助文本?


除了不能生成帮助文本之外,如果我想修改解析器为以下(添加一个默认值,还允许--nested自己解析为Nested Character),这个也会挂掉,而不是达到默认值:

typeParser :: OA.Parser GraphType
typeParser =
  OA.flag' Colour (OA.long "colour")
    <|> OA.flag' Character (OA.long "character")
    <|> (OA.flag' Nested (OA.long "nested") <*> typeParser)
    <|> pure Character

通过将解析器更改为以下内容,我已经能够解决该问题

typeParser :: OA.Parser GraphType
typeParser = iter Nested <$> nestDepthParser <*> unnestedParser
  where
    iter f 0 v = v
    iter f n v = iter f (n - 1) (f v)
    nestDepthParser = OA.option OA.auto (OA.long "nest") <|> pure 0
    unnestedParser =
      OA.flag' Colour (OA.long "colour")
        <|> OA.flag' Character (OA.long "character")
        <|> pure Character

要在此解析器中指定 Nested (Nested Colour) 的值,您需要传递 --nest 2 --colour。 这可行,但并不理想,因为我真的很喜欢“多个--nesting 参数”的命令风格。

【问题讨论】:

    标签: haskell command-line-arguments lazy-evaluation applicative optparse-applicative


    【解决方案1】:

    可以修改问题中的最后一个解析器,以获得“多个--nesting”样式,使用many,如下:

    typeParser :: OA.Parser GraphType
    typeParser = iter Nested <$> nestDepthParser <*> unnestedParser
      where
        iter f 0 v = v
        iter f n v = iter f (n - 1) (f v)
        nestDepthParser = length <$> (many $ OA.flag' () (OA.long "nested"))
        unnestedParser =
          OA.flag' Colour (OA.long "colour")
            <|> OA.flag' Character (OA.long "character")
            <|> pure Character
    

    nestDepthParser 输出一个长度等于 --nested 参数数量的列表 ([()]),然后可以使用该列表将 Nested 应用正确的次数。

    这行得通(与原始代码不同),因为 optparse-applicative 有一个专门的 many 实现,它不会挂起。

    【讨论】:

    • 我在发布问题后的 5 分钟内发现了这一点。话虽如此,欢迎其他答案(我不会自我接受),因为我认为这段代码不像问题中的“递归应用风格”那样优雅,并且如果有另一种更接近的解决方案感兴趣那个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-10
    • 1970-01-01
    • 2017-09-01
    • 2018-09-20
    相关资源
    最近更新 更多