【问题标题】:Howto create a nested/conditional option with optparse-applicative?如何使用 optparse-applicative 创建嵌套/条件选项?
【发布时间】:2014-08-09 23:12:52
【问题描述】:

是否可以使用optparse-applicative 中的方法创建一个haskell 表达式来解析这样的程序选项?

program [-a [-b]] ...

-a-b 是可选标志(使用 switch 实现),限制为仅 -b 选项如果之前输入了 -a 则有效。

谢谢

【问题讨论】:

    标签: haskell applicative optparse-applicative


    【解决方案1】:

    这是可能的,只需稍作调整,有两种不同的方式:

    1. 如果你有-a,你可以制作一个只允许-b的解析器,但你不能坚持-a首先出现,因为optparse-applicative的<*>组合器没有指定订单。
    2. 您可以坚持将-b 选项放在a 选项之后,但您可以通过将a 作为命令来执行此操作,因此您会丢失它前面的-

    Applicative 绝对足够强大,因为不需要检查解析器返回的值来确定是否允许-b,因此不需要>>=;如果-a 成功输出任何,则允许-b

    示例

    我将使用数据类型来表示存在哪些参数,但实际上这些会更有意义。

    import Options.Applicative
    
    data A = A (Maybe B)   deriving Show 
    data B = B             deriving Show
    

    所以我们程序的选项可能包含一个 A,可能有一个 B,并且总是有一个字符串。

    boption :: Parser (Maybe B)
    boption = flag Nothing (Just B) (short 'b')
    

    方式 1:标准组合器 - -b 只能与 -a 一起使用(任何顺序)

    我将使用flag' () (short 'a'),它只是坚持-a 存在,然后使用*> 而不是<*> 来忽略返回值(),只返回boption 解析器返回的任何内容,提供选项-a [-b]。然后我会用A :: Maybe B -> A 标记它,最后我会做整个事情optional,所以你有选项[-a [-b]]

    aoption :: Parser (Maybe A)
    aoption = optional $ A <$> (flag' () (short 'a' ) *> boption)
    
    main = execParser (info (helper <*> aoption) 
                            (fullDesc <> progDesc "-b is only valid with -a")) 
            >>= print
    

    请注意,由于&lt;*&gt; 允许任何顺序,我们可以将-a 放在-b 之后(这不是您所要求的,但可以正常工作并且对某些应用程序有意义)。

    ghci> :main -a 
    Just (A Nothing)
    ghci> :main -a -b
    Just (A (Just B))
    ghci> :main -b -a
    Just (A (Just B))
    ghci> :main -b
    Usage: <interactive> [-a] [-b]
      -b is only valid with -a
    *** Exception: ExitFailure 1
    

    方式2:命令子解析器--b只能跟随a

    您可以使用command 创建一个subparser,它仅在命令字符串存在时才有效。您可以使用它来处理像 cabal 一样的参数,因此 cabal installcabal update 有完全不同的选项。由于command 采用ParserInfo 参数,因此可以使用您可以提供给execParser 的任何解析器,因此您实际上可以任意深度地嵌套命令。遗憾的是,命令不能以- 开头,所以它将是program [a [-b]] ... 而不是program [-a [-b]] ...

    acommand :: Parser A
    acommand = subparser $ command "a" (info (A <$> (helper <*> boption)) 
                                             (progDesc "you can '-b' if you like with 'a'"))
    
    main = execParser (info (helper <*> optional acommand) fullDesc) >>= print
    

    运行如下:

    ghci> :main 
    Nothing
    ghci> :main a 
    Just (A Nothing)
    ghci> :main a -b
    Just (A (Just B))
    ghci> :main -b a
    Usage: <interactive> [COMMAND]
    *** Exception: ExitFailure 1
    

    所以你必须在-b 前面加上a

    【讨论】:

      【解决方案2】:

      恐怕你不能。这正是Applicative 单独无法处理而Monad 可以处理的场景:根据早期结果更改后续操作的结构。在应用计算中,总是需要事先知道“形状”;这有一些优点(比如加速 so 数组组合,或者为命令行选项提供一个很好的可读帮助屏幕),但在这里它限制了您解析“平面”选项。

      虽然 optparse-applicative 的接口也有 Alternative,但它确实允许依赖解析,尽管如 AndrewC 所示以不同的方式。

      【讨论】:

      • 你确定吗?我们确实事先知道这个结构,它只是嵌套的。你不能在 a 上使用command 并使用子解析器来实现 b 吗?
      • 它是固定的,但不是 Applicative。您可以通过添加类似f Bool -&gt; (Bool -&gt; f r) -&gt; f r 的类型来包含一些静态分支行为,即类型受限(&gt;&gt;=)
      • 感谢@leftaroundabout。
      • 对不起,@leftaroundabout,但我不能同意你的推理。 Applicative 对于这种事情已经足够强大了,否则我们将无法使用 applicative 接口来解析以允许 = 仅出现在标识符之后。您无需检查标识符的文本即可知道 = 可以跟随它,因此您不需要绑定。同样在这里,您可以解析 -a 并返回 (),(不是字母 a)。您无需检查值 () 即可知道现在允许 -b,因此您不需要绑定。问题纯粹在于,在这种情况下, 对顺序是不确定的。
      • @leftaroundabout 我在我的第一种方法中使用完全标准的 Applicative & Alternative 组合器,就在一个非可选标志的顶部'(optional 来自 Alternative)。我将“使用 switch 实现”表示“当前使用 switch 实现”。我们可以同意“如果你坚持使用switch,恐怕你不能”?我担心的原因是,正如目前所写的那样,它暗示 Applicative 是一个非常弱的解析接口,但事实并非如此。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多