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 o`listF`

:: (Eq t) => [(t, F i o)] -> F (t, i) (t, o)-- Loops:`loopF`

:: F a a -> F a a`loopLeftF`

:: F (Either loop input) (Either loop output) -> F input output`loopThroughRightF`

:: 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.

*fud*_{2} >==<
*fud*_{1}

, the output of Many of the examples in Chapter 9 contain serial compositions of the form

The library provides the following combinators to capture these cases:mapFf>==<fudfud>==< mapFf

(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:absFsp>==<fudfud>==< absFsp

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`>*<`

:: 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 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`

lr

`loopCompThroughLeftF (`

=l>+<r)`loopThroughRightF`

rl

To create dynamically changing parallel compositions of fudgets, the library provides

wheredynListF :: F (Int, DynFMsg a b) (Int, b)

Above we sawdataDynFMsg 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 to(i,DynCreatefud)

`dynListF`

. The fudget with address Finally, one can send a message(i,DynDestroy)

to(i,DynMsgx)

`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 ` Right`

. The fudget `Left `*fud'*

.