W jakich sytuacjach należy liftIO
użyć? Kiedy używam ErrorT String IO
, lift
funkcja działa w celu podniesienia akcji IO do ErrorT
, więc liftIO
wydaje się zbędna.
haskell
monad-transformers
Lachlan
źródło
źródło
liftIO
do podniesienia do warstwy IO, nawet jeślilift
jest to wystarczające, ponieważ wtedy mogę zmienić stos monady, a kod nadal działa.lift
w tej odpowiedzi (i pytaniu) zakłada się, że pochodzi odControl.Monad.Trans.Class
? Czy nie jestMonadic lifting
to ogólne podnoszenie, jak opisano w pierwszej sekcji tutaj ?liftIO to tylko skrót do monady IO, bez względu na to, w której monadzie się znajdujesz. W zasadzie liftIO to użycie zmiennej liczby wind. Na początku może się to wydawać zbędne, ale użycie liftIO ma jedną wielką zaletę: sprawia, że twój kod IO jest niezależny od faktycznej konstrukcji Monady, więc możesz ponownie użyć tego samego kodu bez względu na liczbę warstw, z których została zbudowana twoja ostateczna Monada (jest to dość ważne podczas pisania transformatora monadowego).
Z drugiej strony, liftIO nie przychodzi za darmo, jak to robi winda: transformatory Monad, których używasz, muszą mieć dla niego wsparcie, np. Monada, w której jesteś, musi być instancją klasy MonadIO, ale większość dzisiejszych Monad to robi. (i oczywiście narzędzie do sprawdzania typów sprawdzi to podczas kompilacji: to jest siła Haskella!).
źródło
Wszystkie poprzednie odpowiedzi dość dobrze wyjaśniają różnicę. Chciałem tylko rzucić trochę światła na wewnętrzne funkcjonowanie, aby łatwiej było zrozumieć, dlaczego
liftIO
nie jest to coś magicznego (dla początkujących Haskellerów, takich jak ja).liftIO :: IO a -> m a
jest mądrym narzędziem, na którym po prostu buduj
lift :: (Control.Monad.Trans.Class.MonadTrans t, Monad m) => m a -> t m a
i najczęściej używany, gdy jest dolna monada
IO
. W przypadkuIO
monady definicja jest dość prosta.class (Monad m) => MonadIO m where liftIO :: IO a -> m a instance MonadIO IO where liftIO = id
To proste ...
liftIO
jest w rzeczywistości tylkoid
dlaIO
monady i zasadniczoIO
jest jedyną, która mieści się w definicji klasy typu.Rzecz w tym, że kiedy mamy typ monady, który składa się z kilku warstw transformatorów monadowych
IO
, lepiej miećMonadIO
instancję dla każdej z tych warstw transformatora monadowego. Na przykładMonadIO
instancjaMaybeT m
wymaga,m
aby była równieżMonadIO
typeklasą.Pisanie
MonadIO
instancji jest również w zasadzie bardzo prostym zadaniem. PonieważMaybeT m
jest zdefiniowany jakoinstance (MonadIO m) => MonadIO (MaybeT m) where liftIO = lift . liftIO
albo za
StateT s m
instance (MonadIO m) => MonadIO (StateT s m) where liftIO = lift . liftIO
oni wszyscy są tacy sami. Wyobraź sobie, że masz 4-warstwowy stos transformatorów, wtedy albo musisz to zrobić,
lift . lift . lift . lift $ myIOAction
albo po prostuliftIO myIOAction
. Jeśli się nad tym zastanowić, każdylift . liftIO
przeniesie Cię o jedną warstwę w dół w stosie, aż zacznie kopać w dół do miejsca,IO
w którymliftIO
jest zdefiniowane jakoid
i kończy się tym samym kodem, co skomponowanelift
powyżej.Więc dlaczego to jest w zasadzie niezależnie od konfiguracji transformator stosu, pod warunkiem, że wszystkie warstwy pod nią są członkowie
MonadIO
iMonadTrans
jedenliftIO
jest w porządku.źródło