1---
2title: Architecture
3order: 1
4---
5
6It may be useful to understand how Wonka's sources work internally
7if you want to write a new operator from scratch or contribute to it.
8
9This section explains how Wonka works internally and how it differs from
10the callbag specification.
11
12## Just Functions
13
14Internally Wonka only uses functions with rather simple signatures to
15make its streams work.
16
17We have sinks on one end, which need to receive values, and sources
18on the other, which need to send values.
19The sink is therefore just a function that we call with values over time.
20This is called a "push" signal.
21
22Because a sink has a start, incoming values, and an end, there are three
23signals that a sink can receive: `Start`, `Push`, and `End`.
24
25``` reason
26type signalT('a) =
27 | Start
28 | Push('a)
29 | End;
30
31type sinkT('a) = (. signalT('a)) => unit;
32```
33
34As shown, the sink is just a function accepting a signal as its argument.
35
36When the stream starts then the sink is called with `Start`,
37Then for every incoming, new value it's called with `Push('a)`,
38and when the stream ends it's finally called with `End`.
39
40Since we want a source to send these values to the sink, the source is
41also just a function and it accepts a sink as its argument.
42
43``` reason
44type sourceT('a) = sinkT('a) => unit;
45```
46
47This is completely sufficient to represent simple "push" streams, where
48values are pushed from the source to the sink. They essentially flow from
49the "top" to the "bottom".
50
51Operators are just functions that transform a source. They take a
52source and some number of arguments and return a new source.
53Internally they may also create a new sink function that wraps the
54sink that their source will be called with.
55
56The type signature of an operator with no other arguments is thus:
57
58``` reason
59type operatorT('a, 'b) = sourceT('a) => sourceT('b);
60/* which is the same as: */
61type operatorT('a, 'b) = (sourceT('a), sinkT('b)) => unit;
62```
63
64## Adding Callbacks
65
66To complete this pattern we're still missing a single piece: callbacks!
67
68Previously, we've looked at how sources are functions that accept sinks, which
69in turn are functions accepting a signal. What we're now missing is what makes
70Wonka's streams also work as iterables.
71
72We'd also like to be able to _cancel_ streams, so that we can interrupt
73them and not receive any more values.
74
75We can achieve this by passing a callback function on when a stream starts.
76In Wonka, a sink's `Start` signal also carries a callback that is used to communicate
77back to the source, making these "talkback signals" flow from the bottom to the top.
78
79``` reason
80type talkbackT =
81 | Pull
82 | Close;
83
84type signalT('a) =
85 | Start((. talkbackT) => unit)
86 | Push('a)
87 | End;
88```
89
90This is like the previous `signalT('a)` definition, but the `Start` signal has the
91callback definition now. The callback accepts one of two signals: `Pull` or `Close`.
92
93`Close` is a signal that will cancel the stream. It tells the source to stop sending
94new values.
95
96The `Pull` signal is a signal that asks the source to send the next value. This is
97especially useful to represent iterables. In practice a user would never send this
98signal explicitly, but sinks would send the signal automatically after receiving the
99previous value from the stream.
100
101In asynchronous streams the `Pull` signal is of course a no-op. It won't do
102anything since we can't ask for asynchronous values.
103
104## Comparison to Callbags
105
106This is the full pattern of Wonka's streams and it's a little different from callbags.
107These changes have been made to make Wonka's streams typesafe. But there's
108also a small omission that makes Wonka's streams easier to explain.
109
110In Callbags, sources don't just accept sinks as their only argument. In fact, in
111callbags the source would also receive three different signals. This can be useful
112to represent "subjects".
113
114A subject is a sink and source combined. It can be used to dispatch values imperatively,
115like an event dispatcher.
116
117In Wonka there's a separate type for subjects however, since this reduces the
118complexity of its streams a lot:
119
120``` reason
121type subjectT('a) = {
122 source: sourceT('a),
123 next: 'a => unit,
124 complete: unit => unit,
125};
126```
127
128Hence in Wonka a subject is simply a wrapper around a source and a `next` and `complete`
129method.