【发布时间】:2014-08-09 23:12:52
【问题描述】:
是否可以使用optparse-applicative 中的方法创建一个haskell 表达式来解析这样的程序选项?
program [-a [-b]] ...
-a 和 -b 是可选标志(使用 switch 实现),限制为仅 -b 选项如果之前输入了 -a 则有效。
谢谢
【问题讨论】:
标签: haskell applicative optparse-applicative
是否可以使用optparse-applicative 中的方法创建一个haskell 表达式来解析这样的程序选项?
program [-a [-b]] ...
-a 和 -b 是可选标志(使用 switch 实现),限制为仅 -b 选项如果之前输入了 -a 则有效。
谢谢
【问题讨论】:
标签: haskell applicative optparse-applicative
这是可能的,只需稍作调整,有两种不同的方式:
-a,你可以制作一个只允许-b的解析器,但你不能坚持-a首先出现,因为optparse-applicative的<*>组合器没有指定订单。-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')
-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
请注意,由于<*> 允许任何顺序,我们可以将-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
-b只能跟随a
您可以使用command 创建一个subparser,它仅在命令字符串存在时才有效。您可以使用它来处理像 cabal 一样的参数,因此 cabal install 和 cabal 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。
【讨论】:
恐怕你不能。这正是Applicative 单独无法处理而Monad 可以处理的场景:根据早期结果更改后续操作的结构。在应用计算中,总是需要事先知道“形状”;这有一些优点(比如加速 so 数组组合,或者为命令行选项提供一个很好的可读帮助屏幕),但在这里它限制了您解析“平面”选项。
虽然 optparse-applicative 的接口也有 Alternative,但它确实允许依赖解析,尽管如 AndrewC 所示以不同的方式。
【讨论】:
command 并使用子解析器来实现 b 吗?
f Bool -> (Bool -> f r) -> f r 的类型来包含一些静态分支行为,即类型受限(>>=)。
optional 来自 Alternative)。我将“使用 switch 实现”表示“当前使用 switch 实现”。我们可以同意“如果你坚持使用switch,恐怕你不能”?我担心的原因是,正如目前所写的那样,它暗示 Applicative 是一个非常弱的解析接口,但事实并非如此。