Today, a friend of mine told me that he was writing a Sudoku solver in Haskell. I could not resist and also wrote a brute-force one. The code is ugly (I was trying to generate as short a program as possible), but it led me to interesting thoughts.
First, here is the code. Beware, you are supposed to know Haskell and monads to understand the comments following the code:
import Control.Monad (msum, mzero, MonadPlus)
import List (elemIndex, (\\), intersperse)
import Maybe (fromJust)
get s (l, ln, c, cn) = [s!!(ll*9+cc) | ll <- [l..l+ln], cc <- [c..c+cn]]
getall n s = [(f 0 9 1, 0, 0, 8), (0, 8, f 9 1 1, 0),
(f 0 27 3, 2, f 9 3 3, 2)] >>= get s
where f m d t = ((if m == 0 then n else n `mod` m) `div` d) * t
solve :: (Eq a, MonadPlus m) => [a] -> a -> [a] -> m [a]
solve g n s = case elemIndex n s of
Nothing -> return s
Just idx -> msum $ (g \\ getall idx s) >>= \x ->
return $ solve g n $ take idx s ++ [x] ++ drop (idx+1) s
showSudoku s = unlines $ "Solution:" : (flip map [0..8] $ \n -> sline n s)
where sline n s = concat $ intersperse " " $
map show $ get s (n, 0, 0, 8)
main = do
s <- getContents
let display = do
choice <- solve [1..9] 0 $ map read $ words s
return $ showSudoku choice
putStr $ fromJust display
The interesting piece here is not the code itself, which is indeed pretty boring and unclear, it is the type declaration of the solve function: its return type is m [a], where [a] represents a completed Sudoku grid and m is a MonadPlus instance. What is the point of choosing a MonadPlus type when, in the main function, the result is coerced to a Maybe (which is a MonadPlus instance) before being printed by the usage of fromJust?
The Maybe type can contain zero or one result. It means that the code, when executed, will either find no solution or return exactly one solution which will then be printed. But what can we change if we want to print all the solutions? (if we start from the empty grid for example)
We are using Haskell after all. Instead of using a Maybe type, we might as well use a list. All right, the list type is also an instance of the MonadPlus class. It can contain nothing (empty list), one result, or more. Let’s change the line containing fromJust in main by:
mapM_ putStr display
The use of mapM_ coerces the use of solve to a list type. Now, all the solutions will be printed, not only one of them.
If you want to try it, create a file called empty containing 9 lines of 9 space-separated zeroes:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
and compile the program (either variant). Then you can feed it the empty sudoku grid:
% ghc -o sudoku sudoku.hs ./sudoku < empty
Monad matters. For real.
Related posts:


Nice post. In case you ever felt like writing some Haskell stuff in French, you might want to know about the nascent Francophone Haskell community. For more details, see http://www.haskell.org/haskellwiki/Fr/Haskell and #haskell.fr on irc.freenode.net.
Now you can exhaustively test your code if you like: http://doorgod.org/sudoku/index-e.html
If you want to try out something new in sudoku, try shendoku, using the sudoku rules but playing two people, one against the other, like battleshipps. They have a free version to download at http://www.shendoku.com/sample.pdf . Anything else they are bringing out or they are working on you can find at http://www.shendoku.com or at they´r blog http://www.shendoku.blogspot.com . Have fun, I am. I specially like one slogan I heard about Shendoku: SUDOKU is like masturbation (on your own)…. SHENDOKU is like sex (it takes two).
Thanks for a nice example of how monadic thinking can be a valuable tool.
Have you seen Richard Bird’s approach to sudoku? He gave it as a Functional Pearl talk at the Portland ICFP (2006, I think); it was also written up as a JFP pearl, apparently in Volume 16, #6.
Hmm, sudoku is naturally array-oriented … there must be a good J solution that fits in one line! (Sorry, I just came here from your “J programming language” blog entry and couldn’t resist.) Nested arrrays, á là APL 2, would be better, if J has them.
Les monads, c’est bien…
Pour une bonne raison d’utiliser les monads, vous pouvez lire cet article où j’expliquais comment le fait d’avoir choisi un type monadique plutôt qu’un simple Maybe augmente automatiquement les possibilités du programme. Nous…