どのような状況でliftIO
を使用する必要がありますか? ErrorT String IO
を使用している場合、lift
関数はIOアクションをErrorT
に持ち上げるように機能するため、liftIO
は不必要に見えます。
lift
は常に「前の」レイヤーから持ち上がります。 2番目のレイヤーから持ち上げる必要がある場合は、lift . lift
等々。
一方、liftIO
は常にIOレイヤーから解放されます(存在する場合、常にスタックの一番下にあります)。したがって、2を超える場合モナドの層はliftIO
に感謝します。
次のラムダの引数のタイプを比較します。
type T = ReaderT Int (WriterT String IO) Bool
> :t \x -> (lift x :: T)
\x -> (lift x :: T) :: WriterT String IO Bool -> T
> :t \x -> (liftIO x :: T)
\x -> (liftIO x :: T) :: IO Bool -> T
liftIOは、IOモナド、どちらのモナドでも、どちらでもかまいません。基本的に、liftIOは可変数のリフトを使用することと同じです。最初は冗長に聞こえるかもしれませんが、liftIOを使用すると大きな利点:IOコードは実際のモナド構造に依存しないため、最終的なモナドが構築されているレイヤーの数に関係なく同じコードを再利用できます(これは、モナド変換子)。
一方、liftのようにliftIOは無料ではありません。使用しているモナドトランスフォーマーは、サポートが必要です。あなたがいるモナドはMonadIOクラスのインスタンスでなければなりませんが、ほとんどのモナドは今日そうです(そしてもちろん、タイプチェッカーはコンパイル時にこれをチェックします:それがHaskellの長所です!)。