Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

A Hello World Example

As a simple example, consider the following three functions:

one , two , three :: W 8 -> W 8
one x   = x + lit 1
two x   = x + lit 2
three x = x + lit 3

Composing these functions (three . two . one) produces a function that adds 6 to its argument. Say we want to pipeline this composition so that, one is applied in the first cycle, two is applied to that result in the second cycle, and three is applied in the third cycle. For a given input i, i + 6 is produced after the third cycle completes. Graphically, this is:

Here is the ReWire code for pipelining one, two, and three so that it behaves as the table above. We will explain each function below. On each cycle, each function one, two, and three take an input and produce an output.

times3 :: (W 8 , W 8 , W 8) -> (W 8 ,  W 8 , W 8)
times3 (i1 , i2 , i3) = (one i1 , two i2 , three i3)

The output for the pipelined device is the third output produced by three:

out3 :: (a , b , c) -> c
out3 (_ , _ , x) = x

Given the current outputs, (o1 , o2 , _) and a new input to the pipelined device, we "shift right":

conn3 :: (W 8 , W 8 , W 8) -> W 8 -> (W 8 ,  W 8 , W 8)
conn3 (o1 , o2 , _) ix = (ix , o1 , o2)

The main engine for all this is pipeline:

pipeline :: Monad m => 
          (ii -> oi) -> (oi -> ox) -> (oi -> ix -> ii) -> oi -> ix -> ReacT ix ox m ()
pipeline f out conn oi ix = do
                              let ii = conn oi ix
                              let o = f ii
                              ix' <- signal (out o)
                              pipeline f out conn o ix'

While pipeline looks complicated, it's really pretty simple. Basically, pipeline times3 out3 conn3 is the pipelined device portrayed above, although one has to provide it with the initial values for the output lines. It is significant to note that pipeline is ReWire code - i.e., it is not a special construct in the language, but, rather, is written in terms of the usual reactive resumption monadic operators.

The non-stalling version of the pipelined three . two . one is:

nostall :: W 8 -> ReacT (W 8) (W 8) Identity ()
nostall = pipeline times3 out3 conn3 (lit 0 , lit 0 , lit 0)

start :: ReacT (W 8) (W 8) Identity ()
start = nostall (lit 99)

Here, the initial values lit 99 and (lit 0 , lit 0 , lit 0) have no significance.

Running this pipelined version in GHCi, for example for inputs [0x1..0xF], one would expect input/output pairs like:

	(0x65,0x65) :> 
	(0x01,0x03) :> /* 1 input */
	(0x02,0x05) :> /* 2 input */
	(0x03,0x69) :> /* 3 input */
	(0x04,0x07) :> /* 7 = 1 + 6 output */	
	(0x05,0x08) :> /* 8 = 2 + 6 output */
	(0x06,0x09) :> /* 9 = 3 + 6 output */
	   ...

There are two versions of the code for this example: