[Haskell-cafe] Tetris

Mathieu Boespflug mboes at tweag.net
Tue Nov 20 10:02:37 EST 2007

> > 2. How do you implement a program that is fundamentally about state
> > mutation in a programming language which abhors state mutation?
> Its not clear games are fundamentally about mutation, anymore than, say,
> window managers are. State we do with monads.

Indeed. Ignoring the concept of a monad for the moment, you could
think of your tetris engine as a function mapping a stream of user
inputs to a stream of resulting states. ie

data Action = None | Rotate | Left | Right | Down
data State = ...

tetris :: [Action] -> [State]
tetris = scanl step

step :: State -> Action -> State
step = ...

where State would probably model the playing grid and where all the
squares are, including the current position of the falling piece.
Remember that scanl has signature equivalent to

scanl :: (s -> a -> s) -> s -> [a] -> [s]

Given some function f on states, an (infinite) list of actions and an
initial state, it will return an (infinite) list of states determined
by f and the list of actions.

The list of inputs from the user can be gotten using the 'getContents'
function, for instance. The 'tetris' function will yield new states
only as new inputs from the user are available; that's the magic of
lazyness. So you could then take this list of states and display each
to the user, as they become available. Your 'main' function would look
something like:

main = do s <- getContents
               lets actions = parseActions s
               mapM_ drawTetrisGrid (tetris actions)

where drawTetrisGrid renders a state to the screen:

drawTetrisGrid :: State -> IO ()

Some people think getContents is evil, for reasons I won't go into, in
which case another solution would probably involve a monad looking
like StateT IO, as Dons suggests. Either way, it really isn't a
problem to implement tetris in a functional style, and Haskell is
certainly up to the task!

Hope this helps,


More information about the Haskell-Cafe mailing list