どのような状況liftIO
で使用する必要がありますか? を使用している場合ErrorT String IO
、lift
関数は IO アクションを に持ち上げるために機能するErrorT
ため、liftIO
余分なようです。
3 に答える
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 を使用することには 1 つの大きな利点があります: IO コードが実際の Monad 構築から独立しているため、最終的な Monad が構築されたレイヤーの数に関係なく、同じコードを再利用できます (これは非常に重要です)。モナド変換子を書くとき)。
一方、lift がそうであるように、liftIO は無料で提供されているわけではありません。使用している Monad トランスフォーマーはそれをサポートしている必要があります。たとえば、使用している Monad は MonadIO クラスのインスタンスである必要がありますが、最近のほとんどの Monad はサポートしています。 (もちろん、型チェッカーはコンパイル時にこれをチェックします。これが Haskell の強みです!)。