Aby zrozumieć, jak używać transformatorów Monada, napisałem poniższy kod bez niego. Odczytuje standardową linię wejściową po linii i wyświetla każdą linię odwróconą, aż napotkana zostanie pusta linia. Zlicza również linie przy użyciu State
, a na końcu wyświetla całkowitą liczbę.Użyj dwóch monad bez transformatora.
import Control.Monad.State
main = print =<< fmap (`evalState` 0) go where
go :: IO (State Int Int)
go = do
l <- getLine
if null l
then return get
else do
putStrLn (reverse l)
-- another possibility: fmap (modify (+1) >>) go
rest <- go
return $ do
modify (+1)
rest
Chciałem dodać bieżący numer linii przed każdym wierszem. Udało mi się to zrobić z StateT
:
import Control.Monad.State
main = print =<< evalStateT go 0 where
go :: StateT Int IO Int
go = do
l <- lift getLine
if null l
then get
else do
n <- get
lift (putStrLn (show n ++ ' ' : reverse l))
modify (+1)
go
Moje pytanie brzmi: w jaki sposób zrobić to samo w wersji bez transformatorów monady?
Zdaję sobie z tego sprawę. Nie szukam wersji bez transformatora monadowego ze względu na wydajność, chcę tylko zobaczyć, jak to będzie wyglądać i nauczyć się czegoś, porównując te dwa, mając nadzieję, że lepiej zrozumie potrzebę transformatorów monadowych. – ByteEater
Ponadto, uwzględnienie zakumulowanych obliczeń stanu dla każdej linii jest czymś, co uważałem i odrzucałem z dokładnie tego powodu: nie wydaje się to właściwym sposobem na użycie monad, proste "Int" byłoby lepszym wyborem. Co więcej, z inkrementacją nie robi to różnicy, ale byłoby błędnie koncepcyjnie, ponieważ obliczenia 'State' są budowane poprzez poprzedzanie akcji' modify (+1) ', więc jeślibym miał np. 'modify (+ length l)', które nie działałoby tak, jak powinno. – ByteEater
@ByteEater, sposób, aby to zrobić bez transformatora Monada, to po prostu przekazać 'Int' ręcznie (denerwujące) lub użyć' IORef' (ograniczone do rzeczy 'IO' i potencjalnie nieefektywne, ale w porządku, jeśli pole jest nieuniknione lub aktualizacje są rzadkie). Nie wiem, czego jeszcze szukasz. – dfeuer