Chips¶
Well now we’re going to change notations again! Here we go:
// writer fibre
chip writer
connector io
pin out: %>int
{
for i in 1..10 do
println$ "Sending " + i.str;
write (io.out,i);
done
}
// reader fibre
chip reader
connector io
pin inp: %<int
{
while true do
var x = read inp;
println$ "Read " + x.str;
done
}
chip doubler
connector io
pin inp: %<int
pin out: %>int
{
while true do
var x = read io.inp;
write (io.out, 2 * x);
done
}
run$ writer |-> doubler |-> reader;
The chips are identical to our previous reader, writer and doubler procedures, we just have some syntactic sugar to make them look like integrated circuit specifications.
A chip can have more than one connector, the connector name is just the parameter name. Each connector can have several pins, the pin names are just the record field names. This syntax is a bit more verbose but it is easier to read and makes the purpose and intended use of the procedures clear.
Standard Chips¶
The library contains a lot of standard chips.
Write block¶
Blocks a writer, deliberately. When a writer writes to a channel connected to this device, nothing happens, so the write blocks.
chip writeblock[T]
connector io
pin inp : %<T
{
}
Read Block¶
Starves a reader, deliberately. When a reader tries to read from a channel connected to this device, nothing happens, so the reader starves.
chip readblock[T]
connector io
pin out: %>T
{
}
Universal sink¶
Reads input forever, but ignores it.
chip sink[T]
connector io
pin inp : %<T
{
while true do
var x = read (io.inp);
C_hack::ignore (x);
done
}
Constant Source¶
Writes the fixed value a forever.
chip source[T] (a:T)
connector io
pin out: %>T
{
while true do
write (io.out, a);
done
}
One shot source¶
Writes the fixed value a once then exits.
chip value[T] (a:T)
connector io
pin out: %>T
{
write (io.out, a);
}
Source from generator¶
Calls a generator to obtain a value, then writes it, repeatedly.
chip generator[T] (g: 1->T)
connector io
pin out: %>T
{
repeat perform write (io.out, g());
}
Source from iterator¶
This chip reads values from an iterator and streams them to output until the iterator returns None. It is a hand optimised version of the less efficient for v in x perform write(io.out,v);.
chip iterate[T] (g: 1->opt[T])
connector io
pin out: %>T
{
again:>
var x = g();
match x with
| Some v =>
write (io.out, v);
goto again;
| None => ;
endmatch;
}
Source from list¶
A list iterator specialised to lists. It returns when all the values in the list have been written out.
chip source_from_list[T] (a:list[T])
connector io
pin out: %>T
{
for y in a perform write (io.out,y);
}
Bound Source from list¶
This routine generates an option stream from the list a. Each value is written as Some v until the list exhausted, then an infinite stream of None[T] is written.
This is a bound stream because the logical content of the stream is terminated by None; that is, the option type is used as a carrier type.
chip bound_source_from_list[T] (a:list[T])
connector io
pin out: %>opt[T]
{
for y in a perform write (io.out,Some y);
while true perform write (io.out,None[T]);
}
Function adaptor¶
One of the most useful chips, the function adaptor reads a stream of values, applying the function f to each value and writing them out as it goes. It is the dual to functional programming map over lists.
chip function[D,C] (f:D->C)
connector io
pin inp: %<D
pin out: %>C
{
while true do
var x = read io.inp;
var y = f x;
write (io.out, y);
done
}
Procedure adaptor¶
Converts a procedure to a sink. Reads values and calls the procedure p on each one.
chip procedure[D] (p:D->0)
connector io
pin inp: %<D
{
while true do
var x = read io.inp;
p x;
done
}
Filter¶
Reads values and writes out that that satisfy the predicate f. Dual to functional programming filter over lists.
chip filter[D,C] (f:D->opt[C])
connector io
pin inp: %<D
pin out: %>C
{
while true do
var x = read io.inp;
match f x with
| Some y => write (io.out, y);
| None => ;
endmatch;
done
}
Filter and map¶
Applies the predicate c, and writes the application of f to values satisfying it out.
chip filter[D,C] (c:D->bool) (f:D->C)
connector io
pin inp: %<D
pin out: %>C
{
while true do
var x = read io.inp;
if c x do
write (io.out, f x);
done
done
}
Sink to List¶
This chip is a sink that reads values and pushes them onto an extenal list identified by a pointer. The list must be initialised before the coroutine is spawned.
chip sink_to_list[T] (p: &list[T])
connector io
pin inp : %<T
{
while true do
var x = read (io.inp);
p <- Cons (x,*p);
done
}
Sink to unique list¶
Same as sink_to_list except the value is only pushed onto the list if it is not already present.
chip sink_to_unique_list[T with Eq[T]] (p: &list[T])
connector io
pin inp : %<T
{
while true do
var x = read (io.inp);
if not (x in *p) perform
p <- Cons (x,*p)
;
done
}
Buffer¶
Perhaps the most important and useful chip, it simply copies its input to its output.
chip buffer [T]
connector io
pin inp: %<T
pin out: %>T
{
while true do
var x = read io.inp;
write (io.out, x);
done
}