The game of life is often implemented by representing the board as a 2D boolean array. This doesn't scale very well to larger boards -- it starts to consume lots of memory, and without some separate mechanism to keep track of a list of live cells, you have to visit each board cell on each iteration. This implementation just keeps a list of live cells to represent the board state; the board "size" is limited only by the maximum of an integer.
import Data.List as L
import Data.Map as M
type Coo = (Int,Int)
type Board = Map Coo Int
moveBoard::Coo->Board->Board
moveBoard (dx,dy) = M.mapKeysMonotonic (\(x,y)->(x + dx, y + dy))
countNeighbors::Board->Board
countNeighbors b =
unionsWith (+) [ moved (-1, -1), moved (0, -1), moved (1, -1),
moved (-1, 0), moved (1, 0),
moved (-1, 1), moved (0, 1), moved (1, 1) ]
where moved (dx, dy) = moveBoard (dx, dy) b
lifeIteration::Board->Board
lifeIteration b = M.union birth survive
where neighbors = countNeighbors b
birth = M.map (const 1) (M.filter (==3) neighbors)
survive = M.intersection b (M.filter (==2) neighbors)
glider = M.fromList $ L.map (\(x,y)->((x,y),1::Int)) ([(1,1),(1,2),(1,3),(2,3),(3,2)]::[(Int,Int)])