parseDescAndTags
目前是一个纯函数,所以它没有办法导致解析失败。为了解决这个问题,我还应该注意在这段代码中:
Add <$> parseDescAndTags <$> partition isTag <$> some (argument str (metavar "DESC"))
运算符<$>
已声明infixl 4
,因此它是左关联的,因此您的表达式等效于:
((Add <$> parseDescAndTags) <$> partition isTag) <$> some (argument str (metavar "DESC"))
你碰巧<$>
在“函数阅读器”仿函数中使用(->) a
,它相当于组合(.)
:
Add . parseDescAndTags . partition isTag <$> some (argument str (metavar "DESC"))
如果要使用ReadM
,则需要使用诸如eitherReader
构造ReadM
动作之类的函数。但问题是你需要将它用作第一个参数argument
而不是str
读者,这是错误的地方,因为some
它在外面,你想根据整个选项的累积结果进行解析失败。
不幸的是,这种上下文相关的解析不是optparse-applicative
设计的。它不Monad
为解析器提供实例。
目前,您的解析器允许标签和描述交错,如下所示(假设isTag = (== ".") . take 1
用于说明):
add some .tag1 description .tag2 text
生产"some description text"
描述和[".tag1", ".tag2"]
标签。这是你想要的,还是你可以使用更简单的格式,比如最后需要所有标签?
add some description text .tag1 .tag2
如果是这样,结果很简单:用 解析至少一个非标签some
,然后用 解析任意数量的标签many
:
addCommand :: Mod CommandFields Arg
addCommand = command "add" (info parser infoMod)
where
infoMod = progDesc "Add a new task"
parser = Add <$> addOpts
addOpts = AddOpts
<$> (unwords <$> some (argument nonTag (metavar "DESC")))
<*> many (argument tag (metavar "TAG"))
nonTag = eitherReader
$ \ str -> if isTag str
then Left ("unexpected tag: '" <> str <> "'")
else Right str
tag = eitherReader
$ \ str -> if isTag str
then Right $ drop 1 str
else Left ("not a tag: '" <> str <> "'")
作为替代方案,您可以使用解析命令行选项,但在运行解析器后对您的选项记录进行optparse-applicative
更复杂的验证。然后,如果您想手动打印帮助文本,您可以使用:
printHelp :: ParserPrefs -> ParserInfo a -> IO a
printHelp parserPrefs parserInfo = handleParseResult $ Failure
$ parserFailure parserPrefs parserInfo ShowHelpText mempty