Simple Wolfram Automata

import Prelude hiding (length, replicate)
import Data.Vector hiding ((!), mapM_, take)
import qualified Data.Vector as V ((!?))
import Data.Monoid
 
type Cell = Char
type Universe = Vector Cell
 
size :: Int
size = 30
 
-- a starting universe with (2 * size + 1) cells
start :: Universe
start = replicate size off <> fromList [on] <> replicate size off
 
on, off :: Cell
on = 'X'
off = ' '
 
-- Guarded index, where out-of-bounds cells are considered off
(!) :: Universe -> Int -> Cell
v ! i = maybe off id (v V.!? i)
 
-- Get the neighbors for a cell by index
neighbors :: Universe -> Int -> Cell -> [Cell]
neighbors v i _ = fmap (v!) [i-1..i+1]
 
-- Calculate the next generation
next :: Universe -> Universe
next v = fmap rule90 $ imap (neighbors v) v
 
-- Wolfram's Rule 90 automata
rule90 :: [Cell] -> Cell
rule90 "XXX" = off
rule90 "XX " = on
rule90 "X X" = off
rule90 "X  " = on
rule90 " XX" = on
rule90 " X " = off
rule90 "  X" = on
rule90 "   " = off
 
main :: IO ()
main = mapM_ putStrLn . take size . fmap toList $ iterate next start