IOモナドから型をアンラップする方法はありますか?
質問
私はこの非常に単純な機能を持っています
import qualified Data.ByteString.Lazy as B
getJson :: IO B.ByteString
getJson = B.readFile jsonFile
readJFile :: IO (Maybe Response)
readJFile = parsing >>= (\d ->
case d of
Left err -> return Nothing
Right ps -> return (Just ps))
where parsing = fmap eitherDecode getJson :: IO (Either String Response)
ここで jsonFile
私のハードドライブ上のファイルへのパスです(do記法の欠如を許していますが、これはより明確に動作することがわかりました)
私の質問は;私が捨てる方法はありますか IO
私はbytestringだけで作業できるようにするための部分ですか?
私はあなたが特定のモナドでパターンマッチをすることができることを知っています Either
と Maybe
それらの値を取得するには、しかし、あなたは似たようなことをすることができますか IO
?
または異なって声を出しました:私が作る方法はありますか readJFile
戻る Maybe Response
IOなしで?
解決
私のコメントを展開するには、次のような方法があります。
getJson :: IO B.ByteString
getJson = B.readFile jsonFile -- as before
readJFile :: B.ByteString -> Maybe Response -- look, no IO
readJFile b = case eitherDecode b of
Left err -> Nothing
Right ps -> Just ps
.
最後に、1つのIOアクション内のすべてを再び結合します。
getAndProcess :: IO (Maybe Response)
getAndProcess = do
b <- getJson
return (readJFile b)
. 他のヒント
すべてが実際にIOを実行する必要がない限り、関数を介して"モナドをドラッグ"する必要はありません。チェーン全体をモナドに持ち上げるだけです fmap
(または liftM
/ liftM2
/ ...).
例えば,
f1 :: B.ByteString -> K
f2 :: K -> I
f3 :: K -> J
f4 :: I -> J -> M
そして、あなたの全体のことは次のようになるはずです
m :: M
m = let k = "f1 getJson"
in f4 (f2 k) (f3 k)
あなたは単に行うことができます
m = fmap (\b -> let k = f1 b
in f4 (f2 k) (f3 k) )
getJson
ちなみに、これはより良く見えるかもしれません do
表記法:
m = do
b <- getJson
return $ let k = f1 b
in f4 (f2 k) (f3 k)
あなたの編集と質問について
私が作る方法はありますか
readJFile
戻るMaybe Response
せずにIO
?
いいえ。, 、それはおそらく動作することはできませんので、 readJFile
IOを行う必要があります。そこから逃げる方法はありません IO
モナドそれから、それはそれの全体のポイントです!(まあ、あります unsafePerformIO
リカルドが言うように、しかし、これは間違いなくそれのための有効なアプリケーションではありません。)
それは開梱のclunkinessだ場合 Maybe
の値は、 IO
モナド、およびそれらの中に括弧を持つ署名は、あなたが見てしたいことがあります MaybeT
トランスフォーマー.
readJFile' :: MaybeT IO Response
readJFile' = do
b <- liftIO getJson
case eitherDecode b of
Left err -> mzero
Right ps -> return ps
一般的に、はい、方法があります。たくさんの「しかし」を伴うが、あります。 Unsafe IO操作: system.io.unsafe 。外部ライブラリを呼び出すときにラッパーを書くのに使用されていますが、通常は通常のHaskellコードでリゾートするものではありません。
基本的には、必要なものを正確に行うunsafePerformIO :: IO a -> a
を呼び出すことができます.IO
パートを除外し、a
型のバック値を戻します。しかし、あなたがドキュメントを見るならば、あなたがシステムにあなた自身を保証する必要がある多くの要件があります。これはすべて同じアイデアで終わった:あなたがIOを介して操作を行っても、答えはIO
で動作していない他のHaskell関数から期待される関数は、入力値に基づいて副作用なしに同じ結果が常に同じ結果を持つべきです。
ここでは、あなたのコードを考えると、ファイルから読んでいるので、これは明らかにではありません。結果タイプのreadJFile
を使用して、他の関数内からIO something
を呼び出して、IOモナド内で作業するだけです。その後、IO
ラッパー内の値(IO
が自分自身にある)で読むことができるようになり、復帰時に結果を別のIO
に再描くことができます。
No, there is no safe way to get a value out of the IO monad. Instead you should do the work inside the IO monad by applying functions with fmap or bind (>>=). Also you should use decode instead of eitherDecode when you want your result to be in Maybe.
getJson :: IO B.ByteString
getJson = B.readFile jsonFile
parseResponse :: B.ByteString -> Maybe Response
parseResponse = decode
readJFile :: IO (Maybe Response)
readJFile = fmap parseResponse getJSON
You could also use do notation if that is clearer to you:
readJFile :: IO (Maybe Response)
readJFile = do
bytestring <- getJson
return $ decode bytestring
Note that you dont even need the parseResponse function since readJFile specifies the type.