Conways game of life in Haskell I started with Haskell some months ago, but didn't really use it since then. As a simple training I implemented Conways game of life. What/How could it be improved?

module Main where

import Control.Monad.State

data CellState = Dead | Alive deriving (Eq)
data Cell = Cell CellState Int deriving (Eq)
data Row = Row [Cell] Int deriving (Eq)
data Grid = Grid [Row]
data NextError = EmptyListError

type GridState a = State Grid a

instance Show Grid where
        show (Grid rows) = unlines $ map show rows

instance Show Row where
        show (Row cells n) = (show n) ++ ": " ++ (unwords $ map show cells)

instance Show Cell where
        show (Cell color _) = show color

instance Show CellState where
        show c = case c of
                Dead -&gt; "_"
                Alive -&gt; "#"

main::IO()
main = print $ run (execState initializeGrid (createGrid 20 20)) 100

run :: Grid -&gt; Int -&gt; [Grid]
run g n = scanl (\s f -&gt; f s) g $ replicate n playRound

initializeGrid :: GridState ()
initializeGrid = do
        setPositionToColor 0 0 Alive
        setPositionToColor 1 1 Alive
        setPositionToColor 1 2 Alive
        setPositionToColor 2 0 Alive
        setPositionToColor 2 1 Alive
        setPositionToColor 4 4 Alive
        setPositionToColor 5 4 Alive
        setPositionToColor 6 4 Alive
        setPositionToColor 5 5 Alive
        setPositionToColor 5 4 Alive
        setPositionToColor 5 6 Alive

createGrid :: Int -&gt; Int -&gt; Grid
createGrid x y = Grid $ map createRow (take x [0,1..]) where
                        createRow = Row (map createCell (take y [0,1..]))
                        createCell = Cell Dead

setPositionToColor :: Int -&gt; Int -&gt; CellState -&gt; GridState ()
setPositionToColor x y color = do
        grid &lt;- get
        let rows = getRows grid
            cells = getCells (rows !! x)
            newState = Grid $ replaceListElement rows x $ Row (replaceListElement cells y $ Cell color y) x
        put newState

getCells :: Row -&gt; [Cell]
getCells (Row c _) = c

getRows :: Grid -&gt; [Row]
getRows (Grid r) = r

replaceListElement :: [a] -&gt; Int -&gt; a -&gt; [a]
replaceListElement list position replacement =
        let (x, _:xs) = splitAt position list
        in x ++ replacement : xs

playRound :: Grid -&gt; Grid
playRound (Grid rows) = Grid $ map (updateRow rows) rows

updateRow :: [Row] -&gt; Row -&gt; Row
updateRow rows row@(Row cells p) = Row (map (updateCell rows row) cells) p

updateCell :: [Row] -&gt; Row -&gt; Cell -&gt; Cell
updateCell rows row cell@(Cell _ position) =
        let upperRowCells = getCells $ previous rows row
            upperCell = upperRowCells !! position
            lowerRowCells = getCells $ next rows row
            lowerCell = lowerRowCells !! position
            neighbourCells = [upperCell, lowerCell] ++
                getNextPrevious (getCells row) cell ++
                getNextPrevious upperRowCells upperCell ++
                getNextPrevious lowerRowCells lowerCell
            countLivingNeighbours = length $ filter (==Alive) $ map getCellColor neighbourCells
            isLiving = getCellColor cell == Alive
        in Cell (calcCellState isLiving countLivingNeighbours) position

getCellColor :: Cell -&gt; CellState
getCellColor (Cell c _) = c

calcCellState :: Bool -&gt; Int -&gt; CellState
calcCellState living livingNeighbours
        | living &amp;&amp; livingNeighbours &lt; 2 = Dead
        | living &amp;&amp; livingNeighbours &lt;= 3 = Alive
        | living &amp;&amp; livingNeighbours &gt; 3 = Dead
        | not living &amp;&amp; livingNeighbours == 3 = Alive
        | otherwise = Dead

getNextPrevious :: Eq a =&gt; [a] -&gt; a -&gt; [a]
getNextPrevious l e = [next l e, previous l e]

next :: Eq a =&gt; [a] -&gt; a -&gt;  a
next l@(x:_) e = case dropWhile (/= e) l of
                   (_:y:_) -&gt; y
                   _ -&gt; x

previous :: Eq a =&gt; [a] -&gt; a -&gt; a
previous l e = case takeWhile (/=e) l of
                [] -&gt; last l
                x -&gt; last x
A few things which came to my mind while reading your code:


The Show instance for CellState might be a bit nicer by not writing out the case expression manually but rather using

instance Show CellState where
    show Dead  = "_"
    show Alive = "#"

You could shorten initializeGrid by using mapM_:

initializeGrid :: GridState ()
initializeGrid = mapM_ (\(x, y) -&gt; setPositionToColor x y Alive)
    [(0,0),(1,1),(1,2),(2,0),(2,1),(4,4),(5,4),(6,4),(5,5),(5,4),(5,6)]

take n [0,1..] is the same as [0,1..n] (looking at createGrid).
callCellState could be shortened by only explicitely testing the conditions determining whether the cell becomes (or remains) alive:

calcCellState :: Bool -&gt; Int -&gt; CellState
calcCellState living livingNeighbours
    | living &amp;&amp; livingNeighbours `elem` [2,3] = Alive
    | not living &amp;&amp; livingNeighbours == 3     = Alive
    | otherwise                               = Dead



On a more general note, seeing how long the updateCell function is and how you had to roll your own next and previous function I do suspect that lists are not the most appropriate data structure for this kind of problem. A vector may be easier to work with.