Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use two monads without a transformer

In order to understand how to use monad transformers, I wrote the following code without one. It reads standard input line by line and displays each line reversed until an empty line is encountered. It also counts the lines using State and in the end displays the total number.

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

I wanted to add the current line number before each line. I was able to do it with 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

My question is: how to do the same in the version without monad transformers?

like image 544
ByteEater Avatar asked Jul 25 '15 16:07

ByteEater


2 Answers

The problem you're having is that the hand-unrolling of StateT s IO a is s -> IO (s, a), not IO (s -> (s, a))! Once you have this insight, it's pretty easy to see how to do it:

go :: Int -> IO (Int, Int)
go s = do
    l <- getLine
    if null l
    then return (s, s)
    else do
        putStrLn (show s ++ ' ' : reverse l)
        go (s+1)
like image 168
Daniel Wagner Avatar answered Sep 20 '22 15:09

Daniel Wagner


You'd just need to run the accumulated state computation on every line. This is O(n²) time, but since your first program is already using O(n) space, that's not too terrible. Of course, the StateT approach is superior in pretty much every way! If you really want to do it "by hand" and not pay an efficiency price, just manage the state by hand instead of building a state transformer at all. You're really not getting any benefit by using State instead of Int in the first program.

like image 20
dfeuer Avatar answered Sep 23 '22 15:09

dfeuer