# Pipelines¶

We are now going to change our way of modelling coroutines again, by using records are arguments instead of tuples.

// writer fibre
proc writer (io: (out: %>int)) ()
{
for i in 1..10 do
println$"Sending " + i.str; write (io.out,i); done } // reader fibre proc reader (io: (inp: %<int)) () { while true do var x = read inp; println$ "Read " + x.str;
done
}

proc doubler (io: (inp: %<int, out: %>int)) ()
{
while true do
write (io.out, 2 * x);
done
}

proc start_network () {
// schannels for communication
var inp1, out1 = mk_ioschannel_pair[int]();
var inp2, out2 = mk_ioschannel_pair[int]();
spawn_fibre$writer (out=out1); spawn_fibre$ doubler (inp=inp1,out=out2);
}

start_network;


Here the coroutines use a single record as a parameter, each field of the record is a channel. This has the advantage that it’s easier to pass the arguments correctly, since they’re named, rather than having to be put in a specific order.

Using this protocol, we can also rewrite start_network:

proc start_network () {
run\$ writer |-> doubler |-> reader;
}


A writer is also called a source. A reader is also called a sink. A loop which reads, does something with the value, then writes, is called a transducer.

If you have a source connected to a series of transducers and ending with a sink, that is called a closed pipeline. The left associative infix pipe operator |-> can be used to connect coroutines into a pipeline. The result of the composition is itself a coroutine.

Not all coroutine networks are pipelines, however they are very common both as whole networks, and also as sub-parts of more complex networks.

Pipelines are dual to monads in functional programming .. but they’re much easier to use and a lot more efficient.