Fudgets are composed in the same way as plain stream processors. Therefore, the description of the stream-processor combinators also holds true for the corresponding fudget combinators. The fudget combinators are presented by name, together with some further combinators, in Chapter 13.
allows sp1 to receive input from both sp2 and sp3. For most practical purposes, sp1 can be regarded as having two input streams, as illustrated in Figure 33. When you usesp1 -==- (sp2 -+- sp3)
getSP
in sp1 to read
from the input streams, messages from sp2 and
sp3 will appear tagged with Left
and Right
,
respectively. You can not directly read selectively from one
of the two input streams, but the Fudget library provides
the combinator
Figure 33. Handling multiple input streams.
which you can use to wait for a selected input. Other input is queued and can be consumed after the selected input has been received. UsingwaitForSP :: (i -> Maybe i') -> (i' -> SP i o) -> SP i o
waitForSP
you can define combinators to
read from one of two input streams:
getLeftSP :: (i1 -> SP (Either i1 i2) o) -> SP (Either i1 i2) o getLeftSP = waitForSP stripLeft getRightSP :: (i2 -> SP (Either i1 i2) o) -> SP (Either i1 i2) o getRightSP = waitForSP stripRight
startupSP :: [i] -> SP i o -> SP i o
that prepends some elements to the input stream of a stream
processor.Note: this implementation leaves a serial composition withstartupSP xs sp = sp -==- putListSP xs idSP
idSP
behind after the messages xs
have been
fed to sp
. An efficient implementation that does
not leave any overhead behind can be obtained by making
use of the actual representation of stream processors.waitForSP
described above.waitForSP :: (i -> Maybe i') -> (i' -> SP i o) -> SP i o waitForSP expected isp = let contSP pending = getSP $ \ msg -> case expected msg of Just answer -> startupSP (reverse pending) (isp answer) Nothing -> contSP (msg : pending) in contSP []
A variation of the loop combinators that has turned out to
be very useful when reusing stream processors is loopThroughRightSP
, illustrated in Figure 34. The key
difference from loopSP
and loopLeftSP
is that
the loop does not go directly back from the output to the
input of a single stream processor. Instead it goes through
another stream processor.
Figure 34. Encapsulation.
A typical situation where loopThroughRightSP
is useful
is when you have a stream processor, spold, that does
almost what you want it to do, but you need it to handle some
new kind of messages. A new stream processor, spnew, can
then be defined. This new stream processor can pass on old
messages directly to spold and handle the new messages in
the appropriate way; on its own, or by translating them to
messages that spold understands. (See also Section 3.1.1 in
[NR94].)
In the composition loopThroughRightSP spnew spold
, all
communication with the outside world is handled by
spnew. spold is connected only to spnew, and is in this sense
encapsulated inside spnew.
The type of loopThroughRightSP
is:
Programming withloopThroughRightSP :: SP (Either oldo newi) (Either oldi newo) -> SP oldi oldo -> SP newi newo
loopThroughRightSP
corresponds to inheritance in object-oriented programming. The encapsulated
stream processor corresponds to the inherited class.
Overridden methods correspond to message constructors that the
encapsulating stream processor handles itself.loopThroughRightSP
using loopLeftSP
together with parallel and serial compositions
as appropriate.loopThroughRightSP :: SP (Either oldo i) (Either oldi o) -> SP oldi oldo ->SP i o loopThroughRightSP spnew spold = loopLeftSP (mapSP post -==- (spold -+- spnew) -==- mapSP pre) where pre (Right input) = Right (Right input) pre (Left (Left newToOld)) = Left newToOld pre (Left (Right oldToNew)) = Right (Left oldToNew) post (Right (Right output)) = Right output post (Right (Left newToOld)) = Left (Left newToOld) post (Left oldToNew) = Left (Right oldToNew)
(-==-) :: SP b c -> SP a b -> SP a c sp1 -==- sp2 = loopThroughRightSP (mapSP route) (sp1 -+- sp2) where route (Right a) = Left (Right a) route (Left (Left c)) = Right c route (Left (Right b)) = Left (Left b)
loopThroughBothSP
,
is a symmetric version ofloopThroughBothSP :: SP (Either l12 i1) (Either l21 o1) -> SP (Either l21 i2) (Either l12 o2) -> SP (Either i1 i2) (Either o1 o2)
loopThroughRightSP
.
A composition loopThroughBothSP sp1 sp2
allows both sp1 and sp2 to communicate with the outside
world and with each other (see Figure 35).
Figure 35. Circuit diagram for
loopThroughBothSP
.
An interesting property of loopThroughBothSP
is
that the circuit diagrams of the more basic combinators, -==-
, -+-
and loopSP
, can be
obtained from the circuit diagram of loopThroughBothSP
by just removing wires. Other combinators are thus easy to
define in terms of loopThroughBothSP
.
nullSP
, putSP
and getSP
) and the operators
that are used to build static networks of stream processors
(-==-
, -*-
, loopSP
, etc.). But
there is in fact no reason why networks must be static. By
using combinators like -==-
and -*-
in a
dynamic way, the number of stream processors can be made to
increase dynamically. The number of stream processors can
also decrease, for example if a component of a parallel
composition dies (since nullSP -*- sp
is equivalent to
sp
).
A practical application of these ideas is discussed in Section 35.4.