Basic tic-tac-toe matrix in Haskell Beginner functional programmer... (but not beginner programmer)

Currently, I have the following code:

import Control.Monad (mapM_)

main = gridMaker 3

gridMaker :: Int -&gt; IO ()
{-Creates a nxn matrix filling the lower triangle with "O"'s
  and filling the rest with "X"s
  Example:
  O --- X --- X
  O --- O --- X
  O --- O --- O
  -}
gridMaker gridSize =
  -- Creates the indicators
  let startingIter = 1
      indicators = indicatorListCreator gridSize startingIter
  -- Print each indicator out to IO
  in mapM_ linePrinter indicators

indicatorListCreator :: Int -&gt; Int -&gt; [[String]]
{- Build the indicators of
   [["O", "X", "X"],["O", "O", "X"] ... and so forth.
   Recursively determines how many iterations we've been through,
   and determines how many "X"s and "O"s we should have
   per each line. -}
indicatorListCreator gridLen iterNum
  | iterNum &gt; gridLen = []
  | otherwise =
    let itersRemaining = gridLen - iterNum
        indicator = replicate iterNum "O" ++
                    replicate itersRemaining "X"
    in
      indicator: indicatorListCreator gridLen (iterNum + 1)

linePrinter :: [String] -&gt; IO ()
{- Takes the indicators and prints each line accordingly. -}
linePrinter [indicator1, indicator2, indicator3] =
  let between = " --- "
      outString = indicator1 ++ between ++
                  indicator2 ++ between ++
                  indicator3
  in putStrLn outString
linePrinter _ = error"Stupidly hardcoded to only show 3x3"


Running and compiling this code results in:

O --- X --- X
O --- O --- X
O --- O --- O


Some of my thoughts...


Is it possible to build indicatorListCreator out of folds?
How can I circumvent hardcoding linePrinter
Is this optimal?
Where should I improve on coding conventions / style?
Any other glaring shortcomings? 


Thank you in advance!
I like that you are trying to not use explicit recursions everywhere. That being said, there are a few standard functions that we could use to get a code similar to the following.

import Data.List(intercalate)

grid :: Int -&gt; [String]
grid n = [intercalate " --- " . take n . drop (n-k) $ osxs | k &lt;- [1..n]]
    where osxs = replicate n "O" ++ replicate n "X"

printGrid :: Int -&gt; IO ()
printGrid = putStr . unlines . grid


Most notably, Data.List.intercalate can be used to generalize your linePrinter, and unlines helps us avoid mapM_.



If you are writing a game, then a Data.Map.Strict.Map could be useful for storing the board's current values.