The different fudget combinators treat the high-level streams in different ways, while the low-level streams are treated in the same way in all combinators. Figure 22 illustrates serial and parallel composition of fudgets.-- Serial composition:>==<
:: F b c -> F a b -> F a c -- Parallel composition:>+<
:: F i1 o1 -> F i2 o2 -> F (Either i1 i2) (Either o1 o2)>*<
:: F i o -> F i o -> F i olistF
:: (Eq t) => [(t, F i o)] -> F (t, i) (t, o) -- Loops:loopF
:: F a a -> F a aloopLeftF
:: F (Either loop input) (Either loop output) -> F input outputloopThroughRightF
:: F (Either oldo newi) (Either oldi newo) -> F oldi oldo -> F newi newo
Figure 22. Serial and parallel composition of fudgets.
Apart from the plumbing combinators listed above, the Fudget library contains further combinators that capture common patterns. Some of these combinators are described in the following sections.
The fudget combinators have corresponding combinators for plain
stream processors, which are discussed in more detail in
Chapter 17. Their names are obtained by replacing the
F
suffix with an SP
, or substituting -...-
for >...<
in the operators.
fud2 >==<
fud1
, the output of fud1 is connected to the input of fud2.
Many of the examples in Chapter 9 contain serial compositions of the form
The library provides the following combinators to capture these cases:mapF f >==< fud fud >==< mapF f
(The library versions of>=^< :: F a b -> (c -> a) -> F c b fud >=^< f = fud >==< mapF f >^=< :: (a -> b) -> F c a -> F c b f >^=< fud = mapF f >==< fud
>^=<
and >=^<
have
more involved definitions to be more efficient.)
Compositions of the form
are also common. The library provides two operators for these special cases:absF sp >==< fud fud >==< absF sp
Some combinators, like>^^=< :: SP b c -> F a b -> F a c sp >^^=< fud = absF sp >==< fud >=^^< :: F b c -> SP a b -> F a c fud >=^^< sp = fud >==< absF sp
popupMenuF
(see Section 10.4),
create parallel compositions of fudgets, but sometimes a serial
composition is instead required. This could be accomplished by
using a loop and an abstract fudget to do the necessary routing,
but the library contains two combinators that do this:
The following equations hold:serCompLeftToRightF :: F (Either a b) (Either b c) -> F a c serCompRightToLeftF :: F (Either a b) (Either c a) -> F b c
serCompRightToLeftF (l >+< r)
=l >==< r
serCompLeftToRightF (l >+< r)
=r >==< l
>+<
can become a bit clumsy. It may then be
more convenient to use listF
,
which allows any type in thelistF :: (Eq a) => [(a, F b c)] -> F (a, b) (a, c)
Eq
class to be used as
addresses of the fudgets to be combined. The restriction is that
the fudgets combined must have the same type. (See
Section 40.4 for a discussion of how a language with
dependent types could eliminate this kind of restriction.)
There is also a combinator for untagged parallel composition:
Input to an untagged parallel composition is sent to both argument fudgets.>*<
:: F i o -> F i o -> F i o
There is a list version of untagged parallel composition as well,
which can easily be defined usinguntaggedListF :: [F a b] -> F a b
>*<
:
whereuntaggedListF = foldr (>*<) nullF
nullF
,
is the fudget that ignores all input and never produces any output.nullF :: F a b
The untagged parallel compositions are not as widely used as the tagged ones. The reason is probably that you usually do not want input to be broadcast to all fudgets in a composition.
There are some further combinators that tend to be useful every once in a while. These are various parallel compositions with the identity fudget:
idRightF :: F a b -> F (Either a c) (Either b c) idLeftF :: F a b -> F (Either c a) (Either c b) bypassF :: F a a -> F a a throughF :: F a b -> F a (Either b a) idRightF fud = fud >+< idF idLeftF fud = idF >+< fud bypassF fud = idF >*< fud throughF fud = idRightF fud >==< toBothF toBothF :: F a (Either a a) toBothF = concatMapF (\ x -> [Left x,Right x]) idF :: F a a idF = mapF id
loopF
,
In the compositionloopF :: F a a -> F a a
loopF fud
, the output from fud is not only
output from the composition, but also sent back to the input of fud.
The most useful loop combinator is probably
loopThroughRightF
. An example use was shown in
Section 9.7 and it is discussed further in
Section 18.2.
Some loop combinators that have been useful are:
These turn parallel compositions into loops. The following equations hold:loopCompThroughRightF :: F (Either (Either a b) c) (Either (Either c d) a) -> F b d loopCompThroughLeftF :: F (Either a (Either b c)) (Either b (Either a d)) -> F c d
loopCompThroughRightF (l >+< r)
=loopThroughRightF l r
loopCompThroughLeftF (l >+< r)
=loopThroughRightF r l
To create dynamically changing parallel compositions of fudgets, the library provides
wheredynListF :: F (Int, DynFMsg a b) (Int, b)
Above we sawdata DynFMsg i o = DynCreate (F i o) | DynDestroy | DynMsg i
listF
that creates tagged parallel
compositions that are static. The combinator dynListF
can
be seen as a variant of listF
with a more elaborate
input message type. When the program starts, dynListF
is
an empty parallel composition. A new fudget fud with address
i can be added to the parallel composition by passing the
messageto(i,DynCreate fud)
dynListF
. The fudget with address i can be
removed from the parallel composition by passing the messageFinally, one can send a message x to an existing fudget with address i by passing the message(i,DynDestroy)
to(i,DynMsg x)
dynListF
.
(The addresses used by dynListF
have been restricted to
the type Int
for efficiency reasons, but in principle,
more general address types could be supported, as for listF
.)
A simpler combinator that allows fudgets to change dynamically
is dynF
:
The fudgetdynF :: F a b -> F (Either (F a b) a) b
dynF fud
starts out behaving like fud,
except that messages to fud should be tagged with Right
. The fudget fud can be replaced by another fudget fud'
by passing in the message Left fud'
.