Haskell 如何使用optparse applicative创建嵌套/条件选项?

Haskell 如何使用optparse applicative创建嵌套/条件选项?,haskell,applicative,optparse-applicative,Haskell,Applicative,Optparse Applicative,可以使用中的方法创建一个haskell表达式来解析如下程序选项吗 program [-a [-b]] ... -a和-b是可选标志(使用开关实现),限制条件是-b选项仅在之前键入-a时有效 谢谢,恐怕不行。这正是Applicative单独无法处理而Monad可以处理的场景:根据早期结果更改后续操作的结构。在应用计算中,“形状”总是需要事先知道;这有一些优点(比如加快so数组组合,或者为命令行选项提供一个可读性很好的帮助屏幕),但在这里它限制了您解析“平面”选项 optparse应用程序的界面也

可以使用中的方法创建一个haskell表达式来解析如下程序选项吗

program [-a [-b]] ...
-a-b是可选标志(使用
开关
实现),限制条件是-b选项仅在之前键入-a时有效


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


optparse应用程序的界面也有
备选方案
,它确实允许依赖解析,尽管是以AndrewC所示的不同方式。

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

  • 如果你有
    -a
    ,你可以制作一个只允许
    -b
    的解析器,但是你不能坚持
    -a
    优先,因为optparse应用程序的
    组合器没有指定顺序
  • 您可以坚持让
    -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'()(短'a')
    ,它只是坚持
    -a
    在那里,但是然后使用
    *>
    而不是
    忽略返回值
    ()
    ,只返回
    选项
    解析器返回的任何内容,并提供选项
    -a[-b]
    。然后我将用
    A::也许B->A
    标记它,最后我将使整个
    成为可选的
    ,这样您就有了选项
    [-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
    
    方式2:命令子parser-
    -b
    只能跟在
    a
    您可以使用
    command
    生成一个
    子parser
    ,该子parser仅在存在命令字符串时有效。您可以像cabal那样使用它来处理参数,以便
    cabal安装
    cabal更新
    具有完全不同的选项。由于
    command
    接受一个
    ParserInfo
    参数,因此可以使用您可以提供给
    execParser
    的任何解析器,因此您实际上可以任意深度嵌套命令。遗憾的是,命令不能以
    -
    开头,因此它将是
    程序[a[-b]]…
    而不是
    程序[-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
    
    acommand::解析器A
    acommand=subparser$命令“a”(信息(a(助手选项))
    (progDesc“如果你愿意,可以用'a'”))
    main=execParser(信息(助手可选的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
    
    ghci>:main
    没有什么
    ghci>:主a
    只是(一无所获)
    ghci>:主a-b
    只是(A(只是B))
    ghci>:主-b a
    用法:[命令]
    ***例外:ExitFailure 1
    

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

    你确定吗?我们事先知道这个结构,它只是嵌套的。你不能在a上使用
    命令
    并使用子parser实现b吗?它是固定的,但不适用。您可以通过添加一个类型(如
    f Bool->(Bool->f r)->f r
    )来包含一些静态分支行为,即一个类型约束
    (>>=)
    。谢谢@leftaroundabout。对不起,@leftaroundabout,但我不能同意您的推理。Applicative对于这类事情来说足够强大,否则我们将无法使用Applicative接口来解析c,从而允许=仅出现在标识符之后。您不需要检查标识符的文本就知道=可以跟在它后面,因此不需要绑定。与此类似,您可以解析-a并返回(),(而不是字母a)。您不需要检查值()就知道现在允许使用-b,因此不需要绑定。问题纯粹是在这个上下文中,顺序是不确定的。@leftarounda关于我在第一个方法中使用完全标准的Applicative&Alternative组合符,就在非可选标志“(
    optional
    来自Alternative)之上。我把“使用开关实现”理解为“当前使用开关实现”。我们能否同意“如果您坚持使用
    开关,恐怕不行”
    ?我担心的原因是,正如目前编写的那样,它意味着Applicative是一个非常弱的解析接口,而事实并非如此。
    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