使用optparse-applicative,我想要一個可選參數,該參數應該是文件的路徑,或者未指定時,stdin
。這裏明顯的選擇是使這個參數類型爲IO Handle
,並且在使用openFile
時傳遞參數。這是我目前有:在optparse-applicative中處理來自openFile的異常ReadM
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
這樣做的問題是,我們沒有正確地從openFile
handle
例外,而是依賴默認行爲未處理的異常(打印錯誤並退出)。這似乎很糟糕。
我認爲更正確的方法將返回Left
與從openFile
的錯誤消息。麻煩的是,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計算。所以看起來你至少需要eitherReader
才能拿String -> IO (Either String a)
或String -> Either (IO String) (IO Handle)
。從我對它們的基本理解來看,這聽起來像是一個monad變換器可以用來包裝ReadM(或者其他方式?)。但是這比我的理解要深入一些,而且我對如何前進感到茫然。
有沒有辦法在optparse-applicative ReadM
中完成handle
IOException
?
代替'數據參數數量= {參數數量輸入:: IO手柄}'也許你需要'數據參數數量=參數數量{輸入::也許(IO處理)}'? –
最簡單的可能的解決方案是用'Maybe FilePath'替換'IO Handle'並在程序參數解析後從文件中讀取。你的解析器變成'參數<$>參數(只是<$> str)(value Nothing)',你可以自由處理文件的存在/不存在。 'main'。反正這個邏輯不是解析器的一部分,解析器的類型反映了這一點。你總是可以使'input'成爲一個參數(即'data Args a = Args {input :: a}')並且有一個函數'Args FilePath - > IO(Args Handle)''。 – user2407038