代码之家  ›  专栏  ›  技术社区  ›  Bailey Parker

处理optparse应用程序ReadM中openFile的异常

  •  3
  • Bailey Parker  · 技术社区  · 8 年前

    使用optparse applicative,我想有一个可选参数,它应该是文件的路径,或者在没有指定的情况下, stdin 。这里显而易见的选择是将此参数类型设置为 IO Handle openFile .以下是我目前拥有的:

    module Main where
    
    import Data.Semigroup ((<>))
    import Options.Applicative
    import System.IO
    
    data Args = Args { input :: IO Handle }
    
    parseArgs = Args <$> argument parseReadHandle (value defaultHandle)
      where defaultHandle = return stdin :: IO Handle
    
    parseReadHandle :: ReadM (IO Handle)
    parseReadHandle = eitherReader $ \path -> Right $ openFile path ReadMode
    
    getArgs :: IO Args
    getArgs = execParser $ info (parseArgs <**> helper) fullDesc
    
    main :: IO ()
    main = run =<< getArgs
    
    run :: Args -> IO ()
    run (Args input) = putStrLn =<< hGetContents =<< input
    

    问题是,我们没有正确地 handle 例外情况 而是依赖于未处理异常的默认行为(打印错误和退出)。这看起来很恶心。

    Left 错误消息来自 打开文件 eitherReader 需要一个 String -> Either String a 所以我们不能这样做:

    {-# LANGUAGE ScopedTypeVariables #-}
    import Control.Exception
    
    parseReadHandle :: ReadM (IO Handle)
    parseReadHandle = eitherReader tryOpenFile
    
    tryOpenFile :: IO (Either String (IO Handle)) -> FilePath
    tryOpenFile path = do
      handle (\(e :: IOException) -> return $ Left $ show e) $ do
        return $ Right $ openFile path ReadMode
    

    tryOpenFile 这不会进行打字检查。我不确定我所要求的是否可能,因为似乎错误消息必须是 IO String ,因为要获得误差,必须执行IO计算。所以至少你需要 其他阅读器 采取行动 String -> IO (Either String a) 或者 String -> Either (IO String) (IO Handle)

    ing an IOException 在optparse应用程序中 ReadM

    1 回复  |  直到 8 年前
        1
  •  4
  •   liminalisht    8 年前

    "I'd like to have an optional argument, which should be a path to a file..."

    好的,那么沿着 Maybe FilePath ? 听起来这可能是你想要的。或同等ADT:

    data Path = StandardInput | Path FilePath
    

    当你说, "The obvious choice here is to make this argument type IO Handle and when an argument is passed in use openFile"

    从命令行解析应该是将要解析的输入转换为适合将来在程序中使用的数据。不要担心在这个阶段打开文件,或者在文件不存在的情况下处理异常,或者以任何其他方式打开文件 这些数据…只是担心这个问题,我的程序的用户是否给了我一个文件路径?也就是说,我有什么数据?其他东西不是(也不应该是) optparse-applicative

    Path 。它可能由每个构造函数的解析器组成。例如。:

    stdInputParser :: Parser Path
    stdInputParser = ...
    
    pathSuppliedParser :: Parser Path
    pathSuppliedParser = ...
    
    pathParser :: Parser Path
    pathParser = pathSuppliedParser <|> stdInputParser
    

    不管怎样,一旦你跑了 execParser ,剩下的是您的 路径 数据类型。所以你将把它作为论点传递给你的 run

    run :: Path -> IO ()
    run StandardInput = ... use stdin here
    run (Path filePath) = ... use openFile here, catch and handle exceptions if the file doesn't exist, etc.
    
    推荐文章