Validation 处理CLI参数语义错误的编程模式或库(即惯用方式)?
我有一个Haskell应用程序,它使用库进行CLI参数解析。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
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."]