Whenever I read about Monad example, they always present IO as a case study.
Are there any examples of monads doing list manipulation which somebody could present? I apr
Monads and list manipulation is even more fun when combined with guard of MonadPlus
and perhaps another monad. As an example, let's solve the classic verbal arithmetic puzzle: SEND + MORE = MONEY
. Each letter must represent a unique digit and the leading digits can't be zero.
For this, we'll construct a monad stack from StateT
and []
:
import Control.Monad
import Control.Monad.List
import Control.Monad.State
-- Verbal Arithmetic monad
type VA a = StateT [Int] [] a
The list monads allows us to branch computations to search all possible ways, and the state is the list of digits that are available for choosing. We can run this monad by giving it all possible digits as:
runVA :: VA a -> [a]
runVA k = evalStateT k [0..9]
We'll need one auxiliary function that branches a computation by picking all available digits, and updating the state with what is left:
pick :: VA Int
pick = do
-- Get available digits:
digits <- get
-- Pick one and update the state with what's left.
(d, ds) <- lift $ split digits
put ds
-- Return the picked one.
return d
where
-- List all possible ways how to remove an element from a list.
split :: [a] -> [(a, [a])]
split = split' []
split' _ [] = []
split' ls (x:rs) = (x, ls ++ rs) : split' (x : ls) rs
Also we'll often need to pick a non-zero digit:
pickNZ :: VA Int
pickNZ = do
d <- pick
guard (d /= 0)
return d
Solving the puzzle is now easy: We can simply implement the addition digit by digit and check using guard
that the digits of the result are equal to the sum:
-- SEND
-- + MORE
-- ------
-- MONEY
money :: [(Int,Int,Int)]
money = runVA $ do
d <- pick
e <- pick
let (d1, r1) = (d + e) `divMod` 10
y <- pick
guard $ y == r1
n <- pick
r <- pick
let (d2, r2) = (n + r + d1) `divMod` 10
guard $ e == r2
o <- pick
let (d3, r3) = (e + o + d2) `divMod` 10
guard $ n == r3
s <- pickNZ
m <- pickNZ -- Actually M must be 1, but let's pretend we don't know it.
let (d4, r4) = (s + m + d3) `divMod` 10
guard $ r4 == o
guard $ d4 == m
return (ds [s,e,n,d], ds [m,o,r,e], ds [m,o,n,e,y])
where
-- Convert a list of digits into a number.
ds = foldl (\x y -> x * 10 + y) 0
The puzzle has exactly one result: (9567,1085,10652)
. (Of course the code can be further optimized, but I wanted it to be simple.)
More puzzles can be found here: http://www.primepuzzle.com/leeslatest/alphameticpuzzles.html.