We achieved simplicity making the world arguments implicit, and using do notation to replace nested function calls with a sequence of function calls.

We can generalize this technique to eliminate code duplication in other contexts. When we do, we call it a monad.

If you’ve been studying denotational semantics. then sooner or later this trick will occur to you, and from this point of view, "monads are just as interesting and important as parentheses"

Otherwise, to paraphrase a famous movie quote, some believe that unfortunately, no one can be told what a monad is; you have to write one for yourself. To this end, we replace our CodeJam module with a monad. Our goal is to be able to hide details so we can write something like:

-- In this hypothetical Google Code Jam problem, each case consists of -- two integers separated by a space, and we're to output their product. import Jam main = jam $ do [m, n] <- getints return $ show $ m * n

While debugging, I found it useful to be able to extract a particular set of cases from an input file, so I added a second list of strings to the state: this second list holds the input read so far for a single test case. Suppose we want to generate a new input file that consists of the first, third and sixth cases of another file. Then we want to be able to temporarily modify our solution to print test inputs instead:

import Jam main = jamCases [1,3,6] $ do [m, n] <- getints return $ show $ m * n

We imagine there are other applications: perhaps we want to print input-output pairs for each case.

The idea is to surreptitiously attach two lists of strings to everything we do. One list holds the input that is about to be read, and the other holds the input we have already read. Our code is just like the IO monad except instead of tacitly modifying a world, we’re tacitly modifying two lists of strings.

module Jam ( jam, jamOff, jamCases, gets, getsn, getints, getintsn, getintegers, getdbls ) where import Control.Monad import Data.List data Jam a = Jam (([String], [String]) -> (a, ([String], [String]))) instance Functor Jam where fmap = liftM instance Applicative Jam where pure k = Jam (\s -> (k, s)) (<*>) = ap instance Monad Jam where Jam c1 >>= fc2 = Jam (\s0 -> let (r, s1) = c1 s0 Jam c2 = fc2 r in c2 s1) return = pure gets :: Jam String gets = Jam (\(x:xs, ys) -> (x, (xs, ys++[x]))) getsn :: Int -> Jam [String] getsn n = replicateM n gets getints :: Jam [Int] getints = getem getintegers :: Jam [Integer] getintegers = getem getintsn :: Int -> Jam [[Int]] getintsn = getemn getdbls :: Jam [Double] getdbls = getem jamOff offset f = interact $ \s -> let Jam fun = f (_n:inp) = lines s n = read _n in unlines $ zipWith (++) (map (\k -> "Case #" ++ show (k+offset) ++ ": ") [1..n]) $ unfoldr (\(xs, _) -> Just $ fun (xs, [])) (inp, []) jam f = interact $ \s -> let Jam fun = f (_n:inp) = lines s n = read _n in unlines $ zipWith (++) (map (\k -> "Case #" ++ show k ++ ": ") [1..n]) $ unfoldr (\(xs, _) -> Just $ fun (xs, [])) (inp, []) jamCases idxs f = interact $ \s -> let Jam fun = f (_n:inp) = lines s n = read _n cases = take n $ unfoldr (\xy -> let (_, (xs, ys)) = fun xy in Just (ys, (xs, []))) (inp, []) in unlines $ (:) (show $ length idxs) $ concatMap ((cases!!) . (+ (-1))) idxs

It turns out we often want to attach some kind of state to a sequence of functions. This is exactly why the State monad was written, and in fact, we could have just defined:

data Jam a = State ([String], [String])

But we should write out a monad in full (even if it is copied from elsewhere!) at least once while learning Haskell.

Our code resembles what we might write if we had stuck with the IO monad.

iogetints :: IO [Int] iogetints = do s <- getLine return $ map read $ words s main = do [m, n] <- iogetints putStrLn $ show $ m * n