Hangi durumlarda liftIOkullanılmalıdır? Kullandığımda ErrorT String IO, liftişlev IO eylemlerini içine almaya çalışıyor ErrorT, bu yüzden liftIOgereksiz görünüyor.
Yanıtlar:
lifther zaman "önceki" katmandan kaldırır. İkinci katmandan kaldırmanız gerekirse, ihtiyacınız olacak lift . liftve benzeri.
Öte yandan, liftIOher zaman IO katmanından (mevcut olduğunda her zaman yığının altındadır) yükselir. Yani, 2'den fazla monad katmanınız varsa, takdir edeceksiniz liftIO.
Aşağıdaki lambdalarda bağımsız değişkenin türünü karşılaştırın:
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, hangi Monad'da olursanız olun, IO Monad'a giden bir kısayoldur. Temel olarak, liftIO, değişken sayıda asansör kullanmaya eşittir. İlk başta bu kulağa gereksiz gelebilir, ancak liftIO kullanmanın büyük bir avantajı vardır: IO kodunuzu gerçek Monad yapısından bağımsız kılar, böylece son Monad'inizin oluşturduğu katman sayısı ne olursa olsun aynı kodu yeniden kullanabilirsiniz (bu oldukça önemlidir bir monad transformatörü yazarken).
Diğer taraftan, liftIO, lift'in yaptığı gibi ücretsiz gelmiyor: Kullandığınız Monad transformatörleri bunun için desteğe sahip olmalıdır, örneğin içinde bulunduğunuz Monad, MonadIO sınıfının bir örneği olmalıdır, ancak günümüzde çoğu Monad var. (ve tabii ki, tür denetleyicisi bunu sizin için derleme sırasında kontrol edecektir: Haskell'in gücü budur!).
Önceki cevapların hepsi farkı oldukça iyi açıklıyor. Sadece iç liftIOişleyişe biraz ışık tutmak istedim, böylece nasıl büyülü olmadığını anlamak daha kolay olsun (benim gibi acemi Haskeller için).
liftIO :: IO a -> m a
sadece üzerine inşa edilen akıllıca bir araçtır
lift :: (Control.Monad.Trans.Class.MonadTrans t, Monad m) => m a -> t m a
ve en çok alt monad olduğunda kullanılır IO. İçin IOmonad 's tanımı oldukça basit.
class (Monad m) => MonadIO m where
liftIO :: IO a -> m a
instance MonadIO IO where
liftIO = id
Bu kadar basit ... liftIOsadece aslında idiçin IOmonad ve temelde IOtip grubun tanımına gelince sadece bir tanesidir.
Mesele şu ki, üzerinde birkaç monad transformatör katmanından oluşan bir monad türüne sahip IOolduğumuzda MonadIO, bu monad transformatör katmanlarının her biri için bir örneğimiz olsa iyi olur. Örneğin, MonadIOörneği de typeclass MaybeT molmasını gerektirir .mMonadIO
Bir yazma MonadIOörneği temelde çok çok basit bir görevdir. İçin MaybeT mbu gibi tanımlanır
instance (MonadIO m) => MonadIO (MaybeT m) where
liftIO = lift . liftIO
yada ... için StateT s m
instance (MonadIO m) => MonadIO (StateT s m) where
liftIO = lift . liftIO
hepsi aynı. 4 katmanlı bir transformatör yığınınız olduğunda ya yapmanız gerektiğini lift . lift . lift . lift $ myIOActionya da sadece yapmanız gerektiğini düşünün liftIO myIOAction. Düşünürseniz, her lift . liftIObiri sizi yığında bir katman aşağıya, ta ki yukarıda oluşturulan kodlar gibi tanımlandığı ve aynı kodla sonuçlandığı IOyere kadar kazıncaya kadar yukarı götürecektir .liftIOidlift
Dolayısıyla, temelde bu, tüm alt katman katmanlarının üyesi olması MonadIOve MonadTranstek bir yeterli liftIOolması koşuluyla, trafo yığını konfigürasyonundan bağımsız olarak temelde budur .
liftIOolsa bile IO katmanına kaldırmak için kullanırımlift, çünkü o zaman monad yığınını değiştirebilirim ve kod hala çalışır.