Haskell Quiz/Count and Say/Solution Dolio

From HaskellWiki


While reading the description for this quiz, I thought it was a perfect problem to make use of the handy clusterBy function Tom Moertel recently discussed on his blog. So, I whipped up this solution to see how it'd work.

module Main (main, say, search) where

import Data.Char
import Data.List
import Data.Maybe
import qualified Data.Map as M

import Control.Arrow
import Control.Monad
import System.Environment

clusterBy :: Ord b => (a -> b) -> [a] -> [[a]]
clusterBy p = M.elems . M.map reverse . M.fromListWith (++) . map (p &&& return)

cluster :: Ord a => [a] -> [[a]]
cluster = clusterBy id

speak :: Int -> String
speak 1  = "ONE"
speak 2  = "TWO"
speak 3  = "THREE"
speak 4  = "FOUR"
speak 5  = "FIVE"
speak 6  = "SIX"
speak 7  = "SEVEN"
speak 8  = "EIGHT"
speak 9  = "NINE"
speak 10 = "TEN"
speak 11 = "ELEVEN"
speak 12 = "TWELVE"
speak 13 = "THIRTEEN"
speak 15 = "FIFTEEN"
speak 18 = "EIGHTEEN"
speak 20 = "TWENTY"
speak 30 = "THIRTY"
speak 40 = "FORTY"
speak 50 = "FIFTY"
speak 60 = "SIXTY"
speak 70 = "SEVENTY"
speak 80 = "EIGHTY"
speak 90 = "NINETY"
speak n | n < 20    = speak (n - 10) ++ "TEEN"
        | n < 100   = speak (n - m) ++ speak m
        | otherwise = error "Unanticipated number."
 where m = n `mod` 10

say :: String -> String
say = intercalate " " . map (\c -> speak (length c) ++ " " ++ take 1 c)
        . cluster . filter isAlpha

search :: String -> Int
search = (1+) . fromJust . search' []
 where search' l s = elemIndex s l `mplus` search' (s:l) (say s)

main = print . search . map toUpper . head =<< getArgs