Converting numbers to words in Haskell
Pattern matching or list element selection?

It's possible to reduce the size of ones, tens, teens and so on if we use !! on a list instead of pattern matching. That's a completely other style though:

ones :: (Integral a, Ord a) =&gt; a -&gt; WordNum
ones n 
  | n &gt; 0 &amp;&amp; n &lt; 10 = onsies !! fromIntegral n
  | otherwise       = error $ "ones: not a one-digit value"
  where
    onsies = words "one two three four five six seven eight nine"


Note that I removed the Show a constraint, because it's not necessary if our value n is in our range. It's only necessary in the error case. Instead, we should add where the error happened. Newer variants of GHC/base include a call-stack, therefore we will know the malfunctioning line, but it's always nice to know at least the function name. It also follows the base-style error messages, e.g.

head (x:_) = x
head []    = error "Prelude.head: empty list"


Either way, back to the code. Several functions are partial. Usually, you want your exported functions to be total, e.g. they never return _|_ (an infinite loop, an error, undefined,...). So instead of the ones above, we could write

ones :: (Integral a, Ord a) =&gt; a -&gt; Maybe WordNum
ones n 
  | n &gt; 0 &amp;&amp; n &lt; 10 = Just $ onsies !! fromIntegral n
  | otherwise       = Nothing
  where
    onsies = words "one two three four five six seven eight nine"


However you wouldn't export all your helper functions either way, so lets keep the error variant. By the way, I've used words "one..." since my keyboard is slightly broken, but it's the same as ["one","two","three",...].

divMod, map and other standard library functions

Sometimes re-inventing the wheel is fun, but usually we want to try to get the most out of our standard library functions.

divMod

There are several occasions where we use both d = a `div` b and m = a `mod` b. If we use both, we can use (d,m) = a `divMod` b. That prevents typos like

    | n &lt; 100   = tens (n `div` 10) ++ ' ' : (groupToWord $ n `mod` 11)
                                                    -- whoops        ^


So let us use it in splitNum:

splitNum :: Integral a =&gt; a -&gt; [a]
splitNum n
    | d == 0    = [n]
    | otherwise = m : splitNum d
  where
    (d,m) = n `divMod` 1000


Note that quotRem will return the same values as divMod on positive numbers and is slightly faster.

zipWith and map

You use zip in your list comprehension just to ++ the zipped elements together. We can achieve this in a single step with zipWith:

[w ++ g | (w,g) &lt;- reverse (zip wordGroups groups)]
= [ w ++ g | (w,g) &lt;- reverse (zipWith (,)  wordGroups groups)]
= [ x      | x     &lt;- reverse (zipWith (++) wordGroups groups)]
= reverse $ zipWith (++) wordGroups groups


toWordGroups is map groupToWord. If we use both functions in numToWord, we end up with

numToWord :: (Integral a, Ord a) =&gt; a -&gt; String
numToWord n
    | n == 0     = "zero"
    | n &gt;= 10^15 = error "numToWord: Doesn't support numbers bigger than trillions"
    | otherwise  = concat $ intersperse ", " $ reverse $ zipWith (++) wordGroups groups
    where
        wordGroups = map groupToWord $ splitNum n