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

标签 validation haskell command-line-arguments optparse-applicative

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

可以做的是实现一堆像下面这样的辅助函数:

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 

然后我像这样使用它:

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")

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

最佳答案

与其在构造之后验证选项记录,也许我们可以使用applicative functor composition。结合参数解析和验证:

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]) OptionsIO 层用于执行文件存在性检查,而 Errors是来自 transformers 的类似验证的 Applicative,它会累积错误消息。运行解析器会产生一个 IO 操作,该操作在运行时会产生一个 Errors [String] Options 值。

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

一些示例构成了 repl:

Λ :main "/tmp" 2
Options {path = "/tmp", count = 2}
Λ :main "/tmpx" 2
["Could not find file."]
Λ :main "/tmpx" 22
["Could not find file.","Incorrect number."]

关于validation - 处理 CLI 参数语义错误的编程模式或库(即惯用方式)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48833162/

相关文章:

haskell - 缺少严格字段的​​ GHC 错误

haskell - 使用 cabal2nix 为不在 nixpkgs 中的包创建本地 nix 环境

python - 可选参数 Python?

language-agnostic - 哪个应该先编码,功能检查还是有效性检查?

multithreading - 如何使用安全异常库捕获异步异常?

c# - ASP.NET MVC - 验证逻辑 - 在哪里放置?

c++ - 如何获取从命令行参数调用的文件的目录?

java - 用于排序整数的 compareTo 方法

javascript - Angular 验证: Restrict server request if user enters invalid email or password

php - Webapp - 限制用户使用的最佳方法