The Haskell State Preprocessor

aka The Haskell Array Preprocessor

The STPP (STate PreProcessor) is a short preprocessor for stateful Haskell programs. It aleviates the pain of performing single array lookup/write/update functions with some syntax to support it. It also supports hash table operations based on the HashTable implementation available from the author. Finally, it supports monadic if and monadic case constructions.

It is lightweight in the sense that it performs no syntactic analysis and is essentially a character transformer. As such, it is limited in some areas, but not significantly. If I find people actually use this, I may be inclined to make it more robost (email me at if you find it useful).

The basic idea is to allow notation in Haskell similar to the standard array[x][y] notation from C/Java/etc. Since brackets are already used by Haskell for lists, we use barred-brackets, so the above would be written array[|x|][|y|].

STPP supports three (or four, depending on how you count) operations: read, assign, update (and use, which is basically read). Since all operations on stateful arrays occur in monads, we use exclusively the "do" notation. You can sort-of use explicit >>= notation, but at your own peril; most of STPP requires the existence of a <- token.

Furthermore, STPP functions must appear on one line completely and there really shouldn't be any trailing things (for instance, a close parenthesis and list for mapM, etc., should go on the next line).

Read

Read operations appear as:
  z <- array[|x|][|y|]
This assumes that array has type XArray t1 (XArray t2 t3) where XArray is a valid Array instance. Furthermore, x should have type t1 and y should have type t2. The desugaring converts this to:
  y <- readArray array (x) >>= (flip readArray) (y) >>= return

Assign

Assign operations appear as:
  array[|x|][|y|] <- expr
Where the type of array, x and y are as above and expr is of type t3, where m is the monad holding the state of array. This gets translated to:
  return expr >>= \stpp_value -> readArray array (x) >>= \stpp_arr -> writeArray stpp_arr (y) stpp_value
Monadic assign operations appear as:
  array[|x|][|y|] <<- expr
Where the type of array, x and y are as above and expr is of type m t3, where m is the monad holding the state of array. This gets translated to:
  expr >>= \stpp_value -> readArray array (x) >>= \stpp_arr -> writeArray stpp_arr (y) stpp_value

Update

Update operations appear as:
  array[|x|][|y|] <-$ expr
This means basically to apply expr to whatever is held in that location of array. The type of expr must be t3 -> m t3, again a monadic operation. This gets desugared into:
  readArray array (x) >>= \stpp_arr -> readArray stpp_arr (y) >>= expr >>= writeArray stpp_arr (y)

Use

Read operations appear as:
  array[|x|][|y|] >>= print
And are semantically the same as read operations, but they don't require the <- token. This gets translated to:
  readArray (array) (x) >>= (flip readArray) (y)  >>= print

Hash Operations

All of what precedes can also be used with hash tables, replacing [|...|] with {|...|}.

Monadic If and Case

Many times you end up writing something like:
  eof <- isEOF h
  if eof then ...
We can make this shorting using the mif construct:
  mif isEOF h then ...
Finally, monadic case expressions alleviate expressions like:
  exists <- ifFileExistsReadIt filepath
  case exists of
    Nothing  -> ...
    Just txt -> ...
and replace them with:
  mcase ifFileExistsReadIt filepath of
    Nothing  -> ...
    Just txt -> ...

Summary

STPP (the Haskell Array Preprocessor) allows you to write programs in Haskell that look like this:
module Main
    where

import Data.Array.IO

main =
    do (arr :: IOArray Int Int) <- newArray (0,4) 0
       arr[|0|] <- return 5
       arr[|1|] <- return 4
       y <- arr[|0|] + 3
       arr[|2|] <-$ return . (+5)
       arr[|3|] <- return (y * 2)
       arr[|4|] <- 2 * arr[|0|] + 3
       getAssocs arr >>= print
Or, even like this:
module Main
    where

import Data.Array.IO

main =
    do (arr :: IOArray Int (IOArray Int (IOArray Int Int))) <- newArray_ (0,4)
       mapM (\i -> do arr[|i|] <- newArray_ (0,4)
	              mapM (\j -> do arr[|i|][|j|] <- newArray (0,4) 0
			    ) [0..4]) [0..4]
       arr[|0|][|1|][|0|] <-$ return . (+1)
       arr[|0|][|1|][|1|] <-$ return . (+2)
       arr[|0|][|1|][|2|] <-$ return . (+3)
       arr[|0|][|1|][|3|] <-$ return . (+4)
       arr[|0|][|1|][|0|] >>= print
       arr[|0|][|1|][|1|] >>= print
       arr[|0|][|1|][|2|] >>= print
       arr[|0|][|1|][|3|] >>= print

You can see in this last program that ") [0..4]) [0..4]" had to go on its own line to avoid confusing the preprocessor.

Download

STPP is only available in source format; it's really small and easy to compile, so there's no reason to distribute binaries. Here's how to compile it:
% ghc --make STPP.hs -o stpp
ghc-5.04: chasing modules from: STPP.hs
Compiling Main             ( STPP.hs, ./STPP.o )
ghc: linking ...
Now, to use it:
% ghc --make -pgmF stpp -F -fglasgow-exts TryAPP.hs -o tryAPP
ghc-5.04: chasing modules from: TryAPP.hs
Compiling  Main             ( TryAPP.hs, ./TryAPP.o )
ghc: linking ...
% ./tryAPP
[(0,5),(1,4),(2,5),(3,16),(4,13)]
You can download a compressed archive containing the STPP application and two sample programs here: stpp.tar.gz. For questions/comments/whatever (if you find it useful), email me at .