Haskell:lift vsliftIO


83

どのような状況liftIOで使用する必要がありますか?を使用している場合ErrorT String IO、このlift関数はIOアクションをに持ち上げるために機能するためErrorTliftIO不要に思えます。

回答:


94

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

34
モナドスタックを変更してもコードが機能するため、liftIO通常liftは、十分な場合でもIOレイヤーにリフトするために使用します。
ジョンL

14
@ジョン:良い点。また、他のモナドではなくIOを持ち上げていることも明らかです。
Roman Cheplyaka 2010年

私は初心者です:liftこの回答(および質問)Control.Monad.Trans.Classでは、からのものであると想定されています、私は推測しますか?ここMonadic liftingの最初のセクション説明されているような、または一般的なリフティングではありません か?
ナワズ

38

liftIOは、どのモナドを使用していても、IOモナドへのショートカットにすぎません。基本的に、liftIOは可変数のリフトを使用することと同じです。最初は冗長に聞こえるかもしれませんが、liftIOを使用することには、1つの大きな利点があります。IOコードが実際のモナド構造に依存しないため、最終的なモナドが構築されているレイヤーの数に関係なく、同じコードを再利用できます(これは非常に重要です)モナド変換子を書くとき)。

一方、liftIOは、liftのように無料で提供されるわけではありません。使用しているモナド変換子は、それをサポートしている必要があります。たとえば、使用しているモナドはMonadIOクラスのインスタンスである必要がありますが、最近のほとんどのモナドはサポートしています。 (そしてもちろん、タイプチェッカーはコンパイル時にこれをチェックします:それがHaskellの強みです!)


2

以前の回答はすべて、違いを非常によく説明しています。liftIO(私のような初心者のハスケラーにとって)魔法のようなものではないことを理解しやすくするために、内部の仕組みに光を当てたかっただけです。

liftIO :: IO a -> m a

ただ上に構築された賢いツールです

lift :: (Control.Monad.Trans.Class.MonadTrans t, Monad m) => m a -> t m a

そして、ほとんどの場合、下のモナドがあるときに使用されますIOIOモナドの場合、その定義は非常に単純です。

class (Monad m) => MonadIO m where
  liftIO :: IO a -> m a

instance MonadIO IO where
  liftIO = id

その単純な...liftIO実際idにはIOモナドの、基本的IOに型クラスの定義に含まれる唯一のものです。

問題は、モナド変換子の複数の層で構成されるモナドタイプIOがあるMonadIO場合、それらのモナド変換子層のそれぞれにインスタンスがある方がよいということです。たとえばMonadIOMaybeT mrequiresのインスタンスmMonadIOも型クラスがあります。

書くMonadIO場合は、あまりにも基本的には非常に簡単な作業です。以下のためにMaybeT m、それは同じように定義されます

instance (MonadIO m) => MonadIO (MaybeT m) where
  liftIO = lift . liftIO

または StateT s m

instance (MonadIO m) => MonadIO (StateT s m) where
  liftIO = lift . liftIO

それらはすべて同じです。4層の変圧器スタックがある場合、実行する必要があるか、lift . lift . lift . lift $ myIOActionまたは単に実行する必要があると想像してくださいliftIO myIOAction。あなたがそれについて考えるならば、それが定義されている場所lift . liftIOまでずっと掘り下げて、上記の構成されたのと同じコードで終了するまで、すべてがあなたをスタックの1つの層に連れて行きます。IOliftIOidlift

したがって、これが基本的に、変圧器スタックの構成に関係なく、すべての下敷き層がのメンバーでMonadIOありMonadTrans、単一の層で問題がない場合の理由liftIOです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.