Hangi durumlarda liftIO
kullanılmalıdır? Kullandığımda ErrorT String IO
, lift
işlev IO eylemlerini içine almaya çalışıyor ErrorT
, bu yüzden liftIO
gereksiz görünüyor.
Yanıtlar:
lift
her zaman "önceki" katmandan kaldırır. İkinci katmandan kaldırmanız gerekirse, ihtiyacınız olacak lift . lift
ve benzeri.
Öte yandan, liftIO
her 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ç liftIO
iş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 IO
monad '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 ... liftIO
sadece aslında id
için IO
monad ve temelde IO
tip 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 IO
olduğ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 m
olmasını gerektirir .m
MonadIO
Bir yazma MonadIO
örneği temelde çok çok basit bir görevdir. İçin MaybeT m
bu 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 $ myIOAction
ya da sadece yapmanız gerektiğini düşünün liftIO myIOAction
. Düşünürseniz, her lift . liftIO
biri sizi yığında bir katman aşağıya, ta ki yukarıda oluşturulan kodlar gibi tanımlandığı ve aynı kodla sonuçlandığı IO
yere kadar kazıncaya kadar yukarı götürecektir .liftIO
id
lift
Dolayısıyla, temelde bu, tüm alt katman katmanlarının üyesi olması MonadIO
ve MonadTrans
tek bir yeterli liftIO
olması koşuluyla, trafo yığını konfigürasyonundan bağımsız olarak temelde budur .
liftIO
olsa 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.