Haskell: Lift - LiftIO Karşılaştırması


83

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:


94

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

34
Genellikle yeterli liftIOolsa bile IO katmanına kaldırmak için kullanırım lift, çünkü o zaman monad yığınını değiştirebilirim ve kod hala çalışır.
John L

14
@John: iyi nokta. Ayrıca, başka bir monad değil de IO'yu kaldırdığınızı açıkça ortaya koyuyor.
Roman Cheplyaka

Ben bir acemiyim: liftbu cevapta (ve sorunun) geldiği varsayılıyor Control.Monad.Trans.Class, sanırım? Değil Monadic liftingveya birinci bölümde açıklandığı gibi genel kaldırma burada ?
Nawaz

38

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!).


2

Ö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 .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.