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
例外をopenFile
処理せず、代わりにハンドルされない例外のデフォルトの動作に依存することです (エラーを出力して終了します)。これはヤバイらしい。
Left
からのエラーメッセージで戻るのがより適切な方法だと思いますopenFile
。問題は、eitherReader
a を期待している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
aString -> IO (Either String a)
または aを取得する必要があるようですString -> Either (IO String) (IO Handle)
。それらの基本的な理解から、ここでモナド変換子を使用して ReadM をラップできるように思えます (またはその逆?)。しかし、それは私の理解が進むよりも少し深いものであり、どのように先行するかについて途方に暮れています.
optparse-applicative でhandle
ingを達成する方法はありますか?IOException
ReadM