首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >编程模式或库(即惯用方式)来处理CLI参数语义错误?

编程模式或库(即惯用方式)来处理CLI参数语义错误?
EN

Stack Overflow用户
提问于 2018-02-16 18:51:47
回答 1查看 294关注 0票数 3

我有一个Haskell应用程序,它使用optparse-applicative库来解析CLI参数。我用于CLI参数的数据类型包含FilePath(文件和目录)、Doubles等等。optparse-applicative可以处理解析错误,但我希望确保某些文件和一些目录存在(或不存在),数字是>= 0等等。

可以做的是实现一组类似于下面这些函数的助手函数:

代码语言:javascript
复制
exitIfM :: IO Bool -> Text -> IO ()
exitIfM predicateM errorMessage = whenM predicateM $ putTextLn errorMessage >> exitFailure 

exitIfNotM :: IO Bool -> Text -> IO ()
exitIfNotM predicateM errorMessage = unlessM predicateM $ putTextLn errorMessage >> exitFailure 

然后我就这样用它:

代码语言:javascript
复制
body :: Options -> IO ()
body (Options path1 path2 path3 count) = do
    exitIfNotM (doesFileExist path1) ("File " <> (toText ledgerPath) <> " does not exist") 
    exitIfNotM (doesDirectoryExist path2) ("Directory " <> (toText skKeysPath) <> " does not exist")
    exitIfM (doesFileExist path3) ("File " <> (toText nodeExe) <> " already exist")
    exitIf (count <= 0) ("--counter should be positive")

我觉得这太特别了,太丑了。而且,我所编写的几乎每个应用程序都需要类似的功能。当我想在实际使用数据类型之前做一些检查时,是否有一些惯用的方法来处理这种编程模式?所涉及的样板越少,效果越好:)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-02-16 21:51:45

也许我们可以使用应用函子组合将参数解析和验证结合起来,而不是在创建选项记录之后验证它:

代码语言:javascript
复制
import Control.Monad
import Data.Functor.Compose
import Control.Lens ((<&>)) -- flipped fmap
import Control.Applicative.Lift (runErrors,failure) -- form transformers
import qualified Options.Applicative as O
import System.Directory -- from directory

data Options = Options { path :: FilePath, count :: Int } deriving Show

main :: IO ()
main = do
    let pathOption = Compose (Compose (O.argument O.str (O.metavar "FILE") <&> \file ->
            do exists <- doesPathExist file
               pure $ if exists
                      then pure file
                      else failure ["Could not find file."]))
        countOption = Compose (Compose (O.argument O.auto (O.metavar "INT") <&> \i ->
            do pure $ if i < 10
                      then pure i
                      else failure ["Incorrect number."]))
        Compose (Compose parsy) = Options <$> pathOption <*> countOption
    io <- O.execParser $ O.info parsy mempty
    errs <- io
    case runErrors errs of
        Left msgs -> print msgs
        Right r -> print r

组合解析器具有Compose (Compose Parser IO) (Errors [String]) Options类型。IO层用于执行文件存在性检查,而Errors则是一个类似验证的应用程序,来自于累积错误消息的转换器。运行解析器会产生一个IO操作,运行时生成一个Errors [String] Options值。

代码有点冗长,但是这些参数解析器可以打包到库中并重用。

一些例子构成了repl:

代码语言:javascript
复制
Λ :main "/tmp" 2
Options {path = "/tmp", count = 2}
Λ :main "/tmpx" 2
["Could not find file."]
Λ :main "/tmpx" 22
["Could not find file.","Incorrect number."]
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48833162

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档