Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/spring-mvc/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Validation 处理CLI参数语义错误的编程模式或库(即惯用方式)?_Validation_Haskell_Command Line Arguments_Optparse Applicative - Fatal编程技术网

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

Validation 处理CLI参数语义错误的编程模式或库(即惯用方式)?,validation,haskell,command-line-arguments,optparse-applicative,Validation,Haskell,Command Line Arguments,Optparse Applicative,我有一个Haskell应用程序,它使用库进行CLI参数解析。CLI参数的数据类型包含FilePaths(文件和目录)、Doubles等。optpass applicative可以处理解析错误,但我希望确保某些文件和目录存在(或不存在),数字为=0等 可以做的是实现一系列帮助器函数,如: exitIfM :: IO Bool -> Text -> IO () exitIfM predicateM errorMessage = whenM predicateM $ putTextLn e

我有一个Haskell应用程序,它使用库进行CLI参数解析。CLI参数的数据类型包含
FilePath
s(文件和目录)、
Double
s等。
optpass 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")
body::Options->IO()
正文(选项路径1路径2路径3计数)=do
exitIfNotM(doesFileExist路径1)(“文件”(toText ledgerPath)“不存在”)
exitIfNotM(doesDirectoryExist路径2)(“目录”(toText skKeysPath)“不存在”)
exitIfM(doesFileExist path3)(“文件”(toText nodeExe)“已存在”)

exitIf(count而不是在构造选项记录后对其进行验证,也许我们可以使用它来组合参数解析和验证:

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

使用monad抽象是正确的方法;但是,我将编写一个类型为
Options->ErrorMessage ValidatedOptions
(带有ErrorMessage和ValidatedOptions的适当定义)的函数;如果需要检查文件的存在性,您可能需要
。->ErrorT IO…
(但这几乎是无用的;现在存在的文件可能不存在——考虑将文件的内容作为验证的一部分读取)。看起来真的很整洁!我不知道
错误
数据类型。虽然乍一看它看起来很神奇…但我甚至不清楚为什么这里不需要
-xapplicivedo
,而且你仍然可以使用
do
@Shersh原来
确实存在,这个解决方案不必要地复杂。li的维护者brary建议在
ReadM
monad中执行验证,该monad已经提供了通知错误的机制
ReadM
是一个纯monad:
newtype ReadM a=ReadM{unReadM::ReaderT String(ParseError除外)a}
。我无法检查该monad中是否存在文件。
Λ :main "/tmp" 2
Options {path = "/tmp", count = 2}
Λ :main "/tmpx" 2
["Could not find file."]
Λ :main "/tmpx" 22
["Could not find file.","Incorrect number."]