Project Icon

optparse-applicative

Haskell命令行选项解析和应用程序构建库

optparse-applicative是一个功能完备的Haskell命令行解析库。它支持参数验证、错误处理、帮助文档生成和shell自动补全。通过简洁的API,开发者可以方便地定义和组合各类选项解析器,包括常规选项、标志、参数和子命令。该库设计灵活,适用于构建复杂的命令行应用程序。它提供了声明式API,用于构建灵活的命令行界面,支持多种选项类型,包括带参数的选项、开关、位置参数和嵌套子命令。通过applicative风格的组合方式,开发者可以轻松创建复杂的命令行程序结构。

optparse-applicative

持续集成状态 Hackage页面(下载和API参考) Hackage-Deps

optparse-applicative是一个Haskell库,用于解析命令行选项,并提供强大的applicative接口来组合它们。

optparse-applicative负责读取和验证传递给命令行的参数,处理和报告错误,生成使用说明行、全面的帮助屏幕,并支持上下文相关的bash、zsh和fish补全。

目录

简介

optparse-applicative的核心类型是Parser

data Parser a

instance Functor Parser
instance Applicative Parser
instance Alternative Parser

Parser a类型的值表示一组选项的规范,当命令行参数成功解析时,将生成类型为a的值。

如果你熟悉像parsecattoparsec这样的解析器组合库,或者JSON解析器aeson,你会对optparse-applicative感到非常熟悉。

如果不熟悉,别担心!你只需要学习一些基本的解析器,以及如何将它们作为ApplicativeAlternative的实例进行组合。

快速入门

这里有一个解析器的简单示例。

import Options.Applicative

data Sample = Sample
  { hello      :: String
  , quiet      :: Bool
  , enthusiasm :: Int }

sample :: Parser Sample
sample = Sample
      <$> strOption
          ( long "hello"
         <> metavar "TARGET"
         <> help "Target for the greeting" )
      <*> switch
          ( long "quiet"
         <> short 'q'
         <> help "Whether to be quiet" )
      <*> option auto
          ( long "enthusiasm"
         <> help "How enthusiastically to greet"
         <> showDefault
         <> value 1
         <> metavar "INT" )

解析器使用applicative风格从一组基本组合子构建而成。在这个例子中,hello被定义为带有String参数的选项,而quiet是一个布尔标志(称为开关),enthusiasm则借助Read类型类被解析为Int类型。

解析器可以这样使用:

main :: IO ()
main = greet =<< execParser opts
  where
    opts = info (sample <**> helper)
      ( fullDesc
     <> progDesc "Print a greeting for TARGET"
     <> header "hello - a test for optparse-applicative" )

greet :: Sample -> IO ()
greet (Sample h False n) = putStrLn $ "Hello, " ++ h ++ replicate n '!'
greet _ = return ()

greet函数是程序的入口点,而opts是程序的完整描述,用于生成帮助文本。helper组合子接受任何解析器,并为其添加help选项。

这个例子中的hello选项是必需的,因为它没有默认值,所以在没有任何参数的情况下运行程序将显示适当的错误消息和简短的选项摘要:

Missing: --hello TARGET

Usage: hello --hello TARGET [-q|--quiet] [--enthusiasm INT]
  Print a greeting for TARGET

使用--help选项运行程序将显示完整的帮助文本,其中包含带有描述的详细选项列表

    hello - a test for optparse-applicative

    Usage: hello --hello TARGET [-q|--quiet] [--enthusiasm INT]
      Print a greeting for TARGET

    Available options:
      --hello TARGET           Target for the greeting
      -q,--quiet               Whether to be quiet
      --enthusiasm INT         How enthusiastically to greet (default: 1)
      -h,--help                Show this help text

基础知识

解析器

optparse-applicative通过其Builder接口提供了许多原始解析器,对应于不同的posix风格选项。这些在下面的专门章节中有详细说明,现在,让我们看几个更多的例子,以了解如何定义解析器。

这里是一个带参数的必需选项的解析器:

target :: Parser String
target = strOption
  (  long "hello"
  <> metavar "TARGET"
  <> help "Target for the greeting" )

可以看到,我们正在定义一个String参数的选项解析器,带有选项名"hello"、元变量"TARGET"和给定的帮助文本。这意味着上面定义的target解析器将要求命令行上有如下选项:

--hello world

元变量和帮助文本将出现在生成的帮助文本中,但不会影响解析器的行为。

传递给选项的属性被称为修饰符,它们使用[半群]操作(<>)进行组合。

target这样带参数的选项被称为常规选项,非常常见。另一种类型的选项是标志,其中最简单的是布尔开关,例如:

quiet :: Parser Bool
quiet = switch ( long "quiet" <> short 'q' <> help "Whether to be quiet" )

这里我们使用了short修饰符来指定选项的单字母名称。这意味着这个开关可以用--quiet-q来设置。

标志与常规选项不同,它们没有参数。它们只是返回一个预定义的值。对于上面的简单开关,如果用户输入了标志,则返回True,否则返回False

还有其他类型的基本解析器,以及几种配置它们的方法。这些在构建器部分中有所介绍。

Applicative

现在我们可以将targetquiet组合成一个接受两个选项并返回组合值的单一解析器。给定一个类型

data Options = Options
  { optTarget :: String
  , optQuiet :: Bool }

现在只需使用 Applicative 的 apply 运算符 (<*>) 来组合之前定义的两个解析器

opts :: Parser Options
opts = Options <$> target <*> quiet

无论序列中哪个解析器出现在前面,选项仍将按照它们在命令行中出现的顺序进行解析。具有这种特性的解析器有时被称为排列解析器

在我们的例子中,像这样的命令行:

--target world -q

将会得到与以下命令行相同的结果:

-q --target world

正是这种特性使我们采用 Applicative 接口而不是 Monadic 接口,因为所有选项必须并行考虑,不能依赖于其他选项的输出。

但请注意,排序顺序仍然有一定的意义,因为它会影响生成的帮助文本。可以通过 lambda 抽象、使用 Arrow 表示法 或利用 GHC 8 的 ApplicativeDo 扩展来轻松实现自定义。

Alternative

通过命令行以不同方式配置程序也很常见。一个典型的例子是可以给程序一个文本文件作为输入,或者直接从标准输入读取。

我们可以在 Haskell 中使用和类型轻松有效地对此进行建模:

data Input
  = FileInput FilePath
  | StdInput

run :: Input -> IO ()
run = ...

现在我们可以为和类型的组成部分定义两个基本解析器:

fileInput :: Parser Input
fileInput = FileInput <$> strOption
  (  long "file"
  <> short 'f'
  <> metavar "FILENAME"
  <> help "Input file" )

stdInput :: Parser Input
stdInput = flag' StdInput
  (  long "stdin"
  <> help "Read from stdin" )

由于 Parser 类型构造函数是 Alternative 的实例,我们可以使用选择运算符 (<|>) 组合这些解析器

input :: Parser Input
input = fileInput <|> stdInput

现在 --file "foo.txt" 将被解析为 FileInput "foo.txt"--stdin 将被解析为 StdInput,但包含两个选项的命令行,如

--file "foo.txt" --stdin

将被拒绝。

由于具有 ApplicativeAlternative 实例,optparse-applicative 解析器还可以与标准组合子组合。例如:optional :: Alternative f => f a -> f (Maybe a) 意味着用户不需要为受影响的 Parser 提供输入。例如,如果用户未提供 output 选项,以下解析器将返回 Nothing 而不是失败:

optional $ strOption
  ( long "output"
 <> metavar "DIRECTORY" )

运行解析器

在我们可以运行 Parser 之前,我们需要将其包装到一个 ParserInfo 结构中,该结构指定了一些仅适用于顶级解析器的属性,例如描述程序功能的标题,以在帮助屏幕中显示。

info 函数将帮助完成这一步骤。在 快速入门 中我们看到

opts :: ParserInfo Sample
opts = info (sample <**> helper)
  ( fullDesc
  <> progDesc "Print a greeting for TARGET"
  <> header "hello - a test for optparse-applicative" )

我们在 opts 后添加的 helper 解析器只是创建了一个显示帮助文本的虚拟 --help 选项。除此之外,我们只是用有意义的值设置了 ParserInfo 结构的一些字段。现在我们有了一个 ParserInfo,终于可以运行解析器了。最简单的方法是在 main 中调用 execParser 函数:

main :: IO ()
main = do
  options <- execParser opts
  ...

execParser 函数处理了所有事情,包括从命令行获取参数、向用户显示错误和帮助屏幕,以及以适当的退出代码退出。

在需要对解析器行为进行更精细控制的情况下,或者如果你想在纯代码中使用它,还有其他运行 ParserInfo 的方法。这些将在 自定义解析和错误处理 中介绍。

构建器

构建器允许你使用方便的基于组合子的语法定义解析器。我们已经看到了构建器的实际应用示例,如 strOptionswitch,我们用它们来为"hello"示例定义 opts 解析器。

构建器总是接受一个 修饰符 参数,它本质上是作用于选项的函数组合,设置属性值或添加功能。

构建器通过从头开始构建选项,并最终将其提升为单一选项解析器,准备使用普通的 ApplicativeAlternative 组合子与其他解析器组合。

有关构建器和修饰符的完整列表,请参阅 Options.Applicative.Builderhaddock 文档

optparse-applicative 中有四种不同类型的选项:常规选项、标志、参数和命令。接下来,我们将逐一介绍这些选项,并描述可用于创建它们的构建器。

常规选项

常规选项是接受单个参数、解析它并返回一个值的选项。

常规选项可以有一个默认值,如果在命令行中未找到该选项,则使用默认值作为结果。没有默认值的选项被视为必需选项,如果未找到则会产生错误。

常规选项可以有名称或(单字符)名称,这决定了选项何时匹配以及如何提取参数。

具有长名称的选项(比如"output")可以在命令行上指定为

--output filename.txt

--output=filename.txt

而短名称选项(比如"o")可以指定为

-o filename.txt

-ofilename.txt

选项可以有多个名称,通常是一个长名称和一个短名称,尽管你可以自由创建具有任意长短名称组合的选项。

返回字符串的常规选项最为常见,可以使用 strOption 构建器创建。例如,

strOption
   ( long "output"
  <> short 'o'
  <> metavar "FILE"
  <> value "out.txt"
  <> help "Write output to FILE" )

创建了一个带有字符串参数的常规选项(可以在帮助文本和文档中称为 FILE),默认值为 "out.txt",长名称为 "output",短名称为 "o"。

常规 option 可以返回任何类型的对象,并接受一个读取器参数,指定如何解析参数。一个常见的读取器是 auto,它需要返回类型的 Read 实例,并用它来解析参数。例如:

lineCount :: Parser Int
lineCount = option auto
            ( long "lines"
           <> short 'n'
           <> metavar "K"
           <> help "Output the last K lines" )

指定一个带有 Int 参数的常规选项。我们在这里添加了显式类型注解,因为没有它解析器的输出类型将是多态的。通常不需要添加类型注解,因为类型通常可以从使用解析器的上下文中推断出来。

关于读取器的更多信息可以在下面找到。

标志

标志就像常规选项,但它不接受任何参数,它在命令行中要么存在要么不存在。

标志有一个默认值和一个激活值。如果在命令行中找到标志,则返回激活值,否则使用默认值。例如:

data Verbosity = Normal | Verbose

flag Normal Verbose
  ( long "verbose"
 <> short 'v'
 <> help "启用详细模式" )

这是一个返回 Verbosity 值的标志解析器。

简单的布尔标志可以使用 switch 构建器来指定,如下所示:

switch
  ( long "keep-tmp-files"
 <> help "保留所有中间临时文件" )

还有一个 flag' 构建器,它没有默认值。这在我们之前的 --stdin 标志示例中演示过,通常用作替代选项的一侧。

flag' 构建器的另一个有趣用途是计算命令行中出现的实例数量,例如,可以按比例指定详细程度设置;以下解析器将计算命令行中 -v 的实例数。

length <$> many (flag' () (short 'v'))

标志可以在单个连字符后一起使用,所以 -vvv-v -v -v 对于上面的解析器都会产生 3。

参数

参数解析器指定一个位置命令行参数。

argument 构建器接受一个读取器参数,并创建一个解析器,每次传递一个命令行参数且读取器成功时都会返回解析后的值。例如

argument str (metavar "FILE")

创建一个接受任何字符串的参数。要接受任意数量的参数,可以将 argument 构建器与 manysome 组合器结合使用:

some (argument str (metavar "FILES"))

注意,默认情况下,以 - 开头的参数被视为选项,不会被 argument 解析器考虑。

然而,解析器总是接受一个特殊参数:--。当在命令行中找到 -- 时,所有后续的单词都会被 argument 解析器考虑,无论它们是否以 - 开头。

参数使用与常规选项相同的读取器

命令

当在命令行中遇到某个特定字符串时,可以使用命令来指定要使用的子解析器。

命令对于实现具有多个功能的命令行程序很有用,每个功能都有自己的选项集,可能还有一些适用于所有功能的全局选项。典型的例子是版本控制系统如 git,或构建工具如 cabal

请注意,出现在命令中的所有解析器需要具有相同的类型。因此,最好使用一个与命令本身结构相同的和类型。例如,对于上面的解析器,你可以定义一个类似这样的类型:

data Options = Options
  { optCommand :: Command
  , ... }

data Command
  = Add AddOptions
  | Commit CommitOptions
  ...

然后可以使用 subparser 构建器(或 hsubparser,它们相同但每个命令都有一个额外的 --help 选项)创建命令,并可以使用 command 修饰符添加命令。例如,

subparser
  ( command "add" (info addCommand ( progDesc "向仓库添加文件" ))
 <> command "commit" (info commitCommand ( progDesc "记录仓库的更改" ))
  )

每个命令都接受一个完整的 ParserInfo 结构,在生成帮助文本时将用于提取该命令的描述。

或者,你可以直接从解析器返回一个 IO 动作,并使用 Control.Monad 中的 join 执行它。

start :: String -> IO ()
stop :: IO ()

opts :: Parser (IO ())
opts = subparser
  ( command "start" (info (start <$> argument str idm) idm)
 <> command "stop"  (info (pure stop) idm) )

main :: IO ()
main = join $ execParser (info opts idm)

修饰符

修饰符SemigroupMonoid 类型类的实例,所以它们可以使用组合函数 mappend(或简单地 (<>))进行组合。由于不同的构建器接受不同的修饰符集,修饰符有一个类型参数,指定哪些构建器支持它。

例如,

command :: String -> ParserInfo a -> Mod CommandFields a

只能与命令一起使用,因为 ModCommandFields 类型参数将阻止它被传递给其他类型选项的构建器。

许多修饰符在这个类型参数上是多态的,这意味着它们可以与任何构建器一起使用。

自定义解析和错误处理

解析器运行器

解析器通过 execParser 系列函数运行 — 从最容易使用到最灵活,这些函数是:

execParser       :: ParserInfo a -> IO a
customExecParser :: ParserPrefs -> ParserInfo a -> IO a
execParserPure   :: ParserPrefs -> ParserInfo a -> [String] -> ParserResult a

当使用 IO 函数时,获取命令行参数以及处理退出代码和失败将自动完成。当使用 execParserPure 时,可以使用以下函数:

handleParseResult :: ParserResult a -> IO a
overFailure :: (ParserHelp -> ParserHelp) -> ParserResult a -> ParserResult a

来正确设置退出代码并显示帮助信息;以及在失败时修改帮助信息(例如添加额外信息)。

选项读取器

选项和参数需要一种方法来解释命令行上传递的字符串到所需的类型。strauto 读取器是最常见的方式,但也可以创建一个不使用 Read 类型类或返回 String 的自定义读取器,并用它来解析选项。自定义读取器是 ReadM 单子中的一个值。

我们提供了 eitherReader :: (String -> Either String a) -> ReadM a 便利函数来帮助创建这些值,其中 Left 将保存解析失败的错误信息。

data FluxCapacitor = ...

parseFluxCapacitor :: ReadM FluxCapacitor
parseFluxCapacitor = eitherReader $ \s -> ...

option parseFluxCapacitor ( long "flux-capacitor" )

也可以直接使用 ReadM,使用 readerAsk 获取命令行字符串,并在 ReadM 单子中使用 readerAbortreaderError 退出并显示错误信息。

eitherReader 的一个很好的特性是它与 attoparsec 解析器很好地组合在一起

import qualified Data.Attoparsec.Text as A
attoReadM :: A.Parser a -> ReadM a
attoReadM p = eitherReader (A.parseOnly p . T.pack)

偏好设置

PrefsMod 可用于自定义使用文本的外观和控制其显示时机;关闭子解析器的回溯;以及开启消歧

要使用这些修改,将它们提供给 prefs 构建器,然后将生成的偏好设置传递给接受 ParserPrefs 参数的解析器运行器之一,如 customExecParser

消歧

可以配置 optparse-applicative 来自动消除长选项前缀的歧义。例如,给定一个带有 --filename--filler 选项的程序 foo,输入

$ foo --fil test.txt

会失败,而输入

$ foo --file test.txt

会成功,并正确识别 "file"filename 选项的无歧义前缀。

选项消歧默认是关闭的。要启用它,请按上述说明使用 disambiguate PrefsMod 修饰符。

以下是一个最小示例:

import Options.Applicative

sample :: Parser ()
sample = () <$
  switch (long "filename") <*
  switch (long "filler")

main :: IO ()
main = customExecParser p opts
  where
    opts = info (helper <*> sample) idm
    p = prefs disambiguate

注意。如果一个选项名是另一个选项的前缀,那么当启用消歧时它将永远不会被匹配。更多详情请参见 #419

自定义帮助屏幕

optparse-applicative 有许多组合器可以帮助自定义使用文本,并确定何时显示它。

progDescheaderfooter 函数可用于指定程序的简要描述或标语,以及围绕生成的选项和命令描述的详细信息。

内部实际上我们使用 prettyprinter 库,如果需要额外的自定义,可以提供文本或 prettyprinter Doc 元素。

要显示使用文本,用户可以在应用 helper 组合器到 Parser 后输入 --help

作者还可以使用 showHelpOnErrorshowHelpOnEmpty 偏好设置,在任何解析器失败时显示帮助文本,或在主程序或其子命令解析开始时命令不完整时显示帮助文本。

即使错误不显示帮助文本,也会显示一个特定的错误消息,指出缺少什么或无法解析什么。

myParser :: Parser ()
myParser = ...

main :: IO ()
main = customExecParser p opts
  where
    opts = info (myParser <**> helper) idm
    p = prefs showHelpOnEmpty

命令组

对于具有多个子命令的程序,一个可能有用的实验性功能是命令组分离。

data Sample
  = Hello [String]
  | Goodbye
  deriving (Eq, Show)

hello :: Parser Sample
hello = Hello <$> many (argument str (metavar "TARGET..."))

sample :: Parser Sample
sample = subparser
       ( command "hello" (info hello (progDesc "Print greeting"))
      <> command "goodbye" (info (pure Goodbye) (progDesc "Say goodbye"))
       )
      <|> subparser
       ( command "bonjour" (info hello (progDesc "Print greeting"))
      <> command "au-revoir" (info (pure Goodbye) (progDesc "Say goodbye"))
      <> commandGroup "French commands:"
      <> hidden
       )

这将在逻辑上分隔两个子解析器的使用文本(如果不使用 commandGroup 修饰符,这些通常会一起出现)。hidden 修饰符抑制了第二个子解析器的元变量在简要使用行中显示,这在某些情况下是可取的。

在这个例子中,我们本质上为我们的解析器创建了同义词,但可以用它来分离常用命令和罕见命令,或安全命令和危险命令。

前面示例的使用文本如下:

Usage: commands COMMAND

Available options:
  -h,--help                Show this help text

Available commands:
  hello                    Print greeting
  goodbye                  Say goodbye

French commands:
  bonjour                  Print greeting
  au-revoir                Say goodbye

Bash、Zsh 和 Fish 补全

optparse-applicative 内置支持 bash、zsh 和 fish shell 中命令行选项和参数的补全。任何使用 execParser 系列函数运行的解析器都会自动扩展几个(隐藏的)选项用于补全系统:

  • --bash-completion-script:这需要程序的完整路径作为参数,并打印一个 bash 脚本,当源入 bash 会话时,将安装必要的机制使 bash 补全工作。为了快速测试,你可以运行类似(对于 PATH 上名为 foo 的程序):

    $ source <(foo --bash-completion-script `which foo`)
    

    通常,--bash-completion-script 的输出应该随程序一起发布,并在安装过程中复制到适当的目录(通常是 /etc/bash_completion.d/);

  • --zsh-completion-script:类似于 zsh;

  • --fish-completion-script:类似于 fish shell;

  • --bash-completion-index--bash-completion-word:补全脚本用于获取给定命令行可能补全列表的内部选项;

  • --bash-completion-enriched:一个标志,告诉补全系统随可能的补全一起发出描述。这用于为 zshfish 提供补全的帮助。

动作和补全器

默认情况下,选项和命令始终会被补全。因此,例如,如果程序 foo 有一个长名称为 output 的选项,输入

$ foo --ou<TAB>

将自动补全为 --output

参数(无论是常规选项的还是顶级的)默认不会被补全。要为参数启用补全,在常规选项或参数上使用以下修饰符之一:

  • completeWith:指定从中选择的可能补全列表;
  • action:指定补全"动作"。动作动态确定可能补全的列表。常见的动作是"file"和"directory";完整的动作列表可以在 [bash 文档]中找到;
  • completer:补全器是一个函数 String -> IO [String],返回给定字符串的所有可能补全。你可以使用这个修饰符为参数指定自定义补全。

补全修饰符可以多次使用:结果补全将调用所有修饰符并合并结果。

内部机制

当使用 execParser 运行解析器时,解析器会扩展 bashCompletionParser,它定义了上述选项。

当触发补全时,补全脚本会使用特殊的 --bash-completion-index--bash-completion-word 选项调用可执行文件。 因此,原始解析器在完成模式下运行,即在不同的 monad 上调用 runParser,该 monad 会跟踪解析器的当前状态,并在处理完所有参数后退出。

完成 monad 在失败时返回解析器的最后状态(如果没有选项匹配),或与选项关联的补全器(如果在获取该选项的参数时失败)。

从中我们生成可能的补全列表,并将它们打印到标准输出。然后补全脚本读取这些内容,并将其放入 COMPREPLY 变量(或其他 shell 的适当替代变量)中。

Arrow 接口

也可以使用 Arrow 语法 来组合基本解析器。

当保存解析结果的结构深度嵌套,或字段顺序与应用解析器的顺序不同时,这特别有用。

使用 Options.Applicative.Arrows 模块中的函数,可以这样写,例如:

data Options = Options
  { optArgs :: [String]
  , optVerbose :: Bool }

opts :: Parser Options
opts = runA $ proc () -> do
  verbosity  <- asA (option auto (short 'v' <> value 0)) -< ()
  let verbose = verbosity > 0
  args       <- asA (many (argument str idm)) -< ()
  returnA -< Options args verbose

其中解析器使用 asA 转换为箭头,结果组合的箭头使用 runA 转回 Parser

查看 tests/Examples/Cabal.hs 可以看到一个稍微复杂一点的例子,展示了如何使用箭头语法定义解析器。

请注意,提供 Arrow 接口只是为了方便。基于 Applicative 的 API 同样具有表现力,尽管在某些情况下使用起来可能会比较麻烦。

Applicative do

有些人可能会发现使用 do 表示法更容易使用 optparse-applicative。然而,由于 Parser 不是 Monad 的实例,这只能在最新版本的 GHC 中使用 ApplicativeDo 扩展来实现。例如,以这种方式指定的解析器可能是:

{-# LANGUAGE RecordWildCards            #-}
{-# LANGUAGE ApplicativeDo              #-}

data Options = Options
  { optArgs :: [String]
  , optVerbose :: Bool }

opts :: Parser Options
opts = do
  optVerbose    <- switch (short 'v')
  optArgs       <- many (argument str idm)
  pure Options {..}

这里我们还使用了 RecordWildCards 扩展来使解析器规范更简洁。如果编译错误提到找不到 Monad 实例,很可能是因为指定的 Parser 无法完全用 Applicative 实现(注意,在 GHC 8.0.1 中有一些关于 ApplicativeDo 的 desugaring 错误,特别是使用 ($) 的函数应用可能不起作用,pure 值应该用括号括起来)。

常见问题

  • 单子式解析?

    如果使用单子式风格,就无法遍历解析器并生成用法字符串,也无法允许选项以任意顺序解析。因此,optparse-applicative 有意不支持以这种方式编写 ParserParser 类型也没有 Monad 实例。

  • 重叠的标志和选项/带可选参数的选项?

    不支持这种情况,因为它可能导致解析歧义。

    例如,如果我们支持并有一个可选值选项 "--foo" 和一个标志 "--bar",那么 "--foo --bar" 是带值 "--bar" 的选项,还是默认值加上开启的标志?如果不是开关而是多个位置字符串参数,第一个字符串是选项的值还是第一个位置参数?

    建议改用 ParserAlternative 实例,并为标志、选项和默认值(使用不同的标志和选项名称)创建一个 flag'。

  • ReadM 错误时回溯?

    解析器结构在解析时预先确定。这意味着如果 ReadM 失败,整个解析也必须失败,我们不能考虑任何替代方案,因为无法保证剩余结构会适合。这种情况的一个偶尔令人困惑的副作用是,不同构造函数的两个位置参数不能在解析器级别组合;相反,这必须在 ReadM 级别完成。例如:

    import Options.Applicative
    
    data S3orFile = S3 BucketKey | File FilePath
    
    s3Read, fileRead :: ReadM S3orFile
    s3Read = S3 <$> ...
    fileRead = File <$> ...
    
    correct :: Parser S3orFile
    correct = argument (s3Read <|> fileRead) idm
    
    incorrect :: Parser S3orFile
    incorrect = argument s3Read idm <|> argument fileRead idm
    

工作原理

应用函子 Parser 本质上是一个异构的 Option 列表或树,使用存在类型实现。

因此,所有选项都是静态已知的(即在解析之前,不一定在运行时之前),可以遍历它们以生成帮助文本。实际上,在显示解析器的用法文本时,我们使用中间树结构。

当我们检查用户输入时,会检查每个参数以确定它是选项、标志还是位置参数。然后搜索解析树以查找匹配项,如果找到,则用值本身替换树的那个叶子。处理完所有输入后,我们检查是否可以生成完整值,如果不能则发出错误。

有关基于简化实现的更详细解释,请参阅这篇博文

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号