Game of Life in Haskell I wondering what people think of this game of life. I don't think it is working properly, but I am interesting in what people think of the general design.

module GameOfLife where
import Data.List

--Our types 
type Coord = (Int, Int)
origin = (0, 0)

--Think of an image more like an infinite manifold  
type DiscreteImage a = (Coord -&gt; a)

--Cat is our "out of bounds"
data GamePiece = Alive | Dead | Cat
    deriving(Show, Eq)

type GameBoard = DiscreteImage GamePiece

type TransitionRule = (GameBoard -&gt; GamePiece)

under_population = make_population_transition Alive Dead (&lt;2)
overcrowding     = make_population_transition Alive Dead (&gt;3)
birth            = make_population_transition Dead Alive (==3)

--Order is not important. Only one transition applies per peice, 
--the others operate like id 
transition_rule :: TransitionRule
transition_rule image = foldl (\image f -&gt; f image) image 
    [
        under_population,
        overcrowding,
        birth
    ] origin

--return a new gameboard that applies the transition rule to every 
--spot on the gameboard
step :: TransitionRule -&gt; GameBoard -&gt; GameBoard
step trans gb = trans . offset gb  

--apply the tranisition rule n number of times
simulate :: Int -&gt; TransitionRule -&gt; GameBoard -&gt; GameBoard
simulate count trans gb = foldl apply_step gb steps where 
  steps = replicate count (step trans)
  apply_step gb s = s gb

--translate the image
offset :: DiscreteImage a -&gt; Coord -&gt; Coord -&gt; a
offset image (offsetX, offsetY) (x, y) = image (offsetX + x, offsetY + y) 

--make a square of coords
square_coords :: Int -&gt; [Coord] 
square_coords radius = [(x, y) | x &lt;- diameter_list, y &lt;- diameter_list] where
        diameter_list = [(negate radius) .. radius]

--return a neighborhood with the center missing
punctured_neighborhood :: Int -&gt; DiscreteImage a -&gt; [a] 
punctured_neighborhood size image = map image $ filter (not . (==origin)) $ square_coords size 


--a little test
main = do 
    let (width, height) = get_bounds test_list
    let board1 = simulate 1 transition_rule test_board
    putStr $ unlines [ unwords [ ppr_peice $ board1 (x, y) | x &lt;- [0..(width -1)]] | 
                y &lt;- [0..(height - 1)]]

test_list = 
    [
        [Alive, Dead, Alive, Dead],
        [Alive, Dead, Dead, Alive],
        [Dead, Alive, Dead, Alive],
        [Dead, Alive, Alive, Alive]
    ]

test_board = image_from_lists test_list Cat

--helpers
population_count = length . filter is_Alive . punctured_neighborhood 1

--does nothing if the origin peice /= from, otherwise test for replacement
make_population_transition from to condition image | image origin == from = 
    if (condition . population_count) image 
            then replace image origin to 
            else image
make_population_transition from to test image | otherwise = image

replace image coord_to_replace value coord = if coord_to_replace == coord 
                                                 then value 
                                                 else image coord

bi f g h x = f x `h` g x

is_Alive Alive = True
is_Alive _ = False

from_Bool True = Alive
from_Bool False = Dead

image_from_lists xs default_value coord@(x, y) = if is_in_bounds1 coord
    then (xs !! x) !! y
    else default_value where
      bounds = get_bounds xs
      is_in_bounds1   = is_in_bounds bounds 

get_bounds xs = if null xs
    then (0, 0)
    else (length xs, length $ head xs)

is_in_bounds (xb, yb) (x, y) = (y &lt; yb &amp;&amp; y &gt; -1) &amp;&amp; (x &lt; xb &amp;&amp; x &gt; -1)

ppr_peice Dead = "O"
ppr_peice Alive = "X"
ppr_peice Cat = "C"
Refactor's answer is excellent and captures most points. One thing in your code which sticks out is the overuse of if. E.g. consider

get_bounds xs = if null xs
    then (0, 0)
    else (length xs, length $ head xs)


I think

getBounds [] = (0,0)
getBounds xs = (length xs, length $ head xs)


is much clearer. Or this, which looks really strange:

make_population_transition from to condition image | image origin == from = 
    if (condition . population_count) image 
            then replace image origin to 
            else image
make_population_transition from to test image | otherwise = image


You should write

make_population_transition from to condition image 
     | image origin == from &amp;&amp; 
       (condition . population_count) image = replace image origin to 
     | otherwise = image