1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
//! rspl is a stream-processor language based on the one discussed in [Generalising Monads to Arrows](https://www.sciencedirect.com/science/article/pii/S0167642399000234) using Rust as a meta-language.
//!
//! ## Design
//!
//! Essentially, rspl is a way to encode functions from streams to streams such that control is syntactically explicit (like in ordinary continuation-passing style), refining the orthodox functional approach to stream processing with combinators like 'map'.
//! More precisely, the idea of this stream-processor language is to split the processing of streams into two parts:
//! One part for reading (getting) the first element of an input stream to direct further processing.
//! Another part for writing (putting) something to the output stream and offering to process some input stream if needed.
//! Combining these parts in various ways allows you to flexibly construct stream processors as programs.
//! The following graphic illustrates how those two different kinds of stream processors ('getting' and 'putting') work (a textual description is provided in the docs of [`StreamProcessor`]):
//!
//! <pre>
//! h--t1--t2--t3--... ha--t1--t2--t3--...
//! - -
//! | |
//! | Get(h |-> [SP](h)) | Put(hb, LAZY-[SP])
//! | |
//! v |
//! t1--t2--t3--... | t1--t2--t3--...
//! - | -
//! | v |
//! | [SP](h) = Get(_) hb--| LAZY-[SP]() = Get(_)
//! | |
//! v v
//! ... ...
//!
//!
//! h--t1--t2--t3--... ha--t1--t2--t3--...
//! - -
//! | |
//! | Get(h |-> [SP](h)) | Put(hb, LAZY-[SP])
//! | |
//! v |
//! h--t1--t2--t3--... | ha--t1--t2--t3--...
//! - | -
//! | v |
//! | [SP](h) = Put(_, _) hb--| LAZY-[SP]() = Put(_, _)
//! | |
//! v v
//! ... ...
//! </pre>
//!
//! Remarkably, the language constructs are somewhat dual and loosely correspond to (dual) programming paradigms:
//! - The `Get`-construct relates to event-driven programming, as it eagerly reacts to input (events).
//! - The `Put`-construct relates to demand-driven[^1] programming, as it iteratively generates output (demands) as needed.
//!
//! This will be discussed further in the [Examples](#examples) section.
//!
//! ## Usage
//!
//! To program an rspl-[`StreamProcessor`], you just have to compose the constructors [`StreamProcessor::Get`]/[`get`](`StreamProcessor::get`) and [`StreamProcessor::Put`]/[`put`](`StreamProcessor::put`) in the right way.
//! For a somewhat more high-level programming experience, you might wish to look at the [`combinators`] module.
//! The program can then be evaluated with the [`eval`](`StreamProcessor::eval`) method on some kind of input stream.
//! The 'kind' of input stream is either your own implementation of the [`Stream`] interface or one
//! from the submodules of the [`streams`] module.
//! Either way, as a result, evaluation produces an [`InfiniteList`] (lazily).
//! To observe streams - and in particular infinite lists - you can deconstruct them with the [`head`](`Stream::head`) and [`tail`](`Stream::tail`) methods of the stream interface.
//! Moreover, there are various functions that help with the destruction and construction of streams.
//!
//! # Examples
//!
//! As alluded to in the [Design](#design) section, rspl supports orthodox 'combinator-driven' stream processing, as is known from list processing with combinators like [`compose`](`combinators::compose`), [`filter`](`combinators::filter`), and [`map`](`combinators::map`).
//! For example, it is possible to first filter some 'bad' elements out of a stream in order to safely iterate some function over the resulting stream afterwards in a combinatorial way.
//! Such usage of rspl looks like this:
//!
//! ```
//! use rspl::combinators::{compose, filter, map};
//! use rspl::streams::infinite_lists::InfiniteList;
//! use rspl::streams::Stream;
//! use rspl::StreamProcessor;
//!
//! let is_greater_zero = |n: &usize| *n > 0;
//! let minus_one = |n: usize| n - 1;
//!
//! let zeroes = compose(filter(is_greater_zero), map(minus_one))
//! .eval(InfiniteList::cons(0, || InfiniteList::constant(1)));
//!
//! assert_eq!(*zeroes.head(), 0);
//! ```
//!
//! More interestingly, rspl can also serve as a framework for the nifty idea of
//! - event-driven programming with state machines as suggested [here](https://barrgroup.com/Embedded-Systems/How-To/State-Machines-Event-Driven-Systems).
//! Abstractly, this usage of rspl looks as follows:
//!
//! ```
//! use rspl::streams::infinite_lists::InfiniteList;
//! use rspl::streams::Stream;
//! use rspl::StreamProcessor;
//!
//! #[derive(Copy, Clone)]
//! enum Event {
//! Event1,
//! Event2,
//! }
//!
//! fn action() -> bool {
//! true
//! }
//!
//! fn state_1<'a>() -> StreamProcessor<'a, Event, bool> {
//! fn transition<'a>(event: Event) -> StreamProcessor<'a, Event, bool> {
//! match event {
//! Event::Event1 => StreamProcessor::put(action(), state_1),
//! Event::Event2 => state_2(),
//! }
//! }
//!
//! StreamProcessor::get(transition)
//! }
//!
//! fn state_2<'a>() -> StreamProcessor<'a, Event, bool> {
//! fn transition<'a>(event: Event) -> StreamProcessor<'a, Event, bool> {
//! match event {
//! Event::Event1 => state_1(),
//! Event::Event2 => StreamProcessor::put(false, state_2),
//! }
//! }
//!
//! StreamProcessor::get(transition)
//! }
//!
//! let event_loop = state_2().eval(InfiniteList::constant(Event::Event1));
//!
//! assert!(event_loop.head());
//! ```
//!
//! A slightly more concrete example using that pattern is available as an [integration test](https://github.com/shtsoft/rspl/blob/master/tests/events.rs).
//! A full-blown concrete example of a pelican crossing can be found [here (as an .md file)](https://github.com/shtsoft/rspl/blob/master/examples/pelican.md) and [here (as an .rs file)](https://github.com/shtsoft/rspl/blob/master/examples/pelican.rs).
//! Notably, it uses rspl to encode effectful hierarchical state machines with a capability-passing-inspired effect-handling mechanism.
//! - demand-driven programming with generators as suggested [here](https://www.cse.chalmers.se/~rjmh/Papers/whyfp.pdf).
//! Abstractly, this usage of rspl looks as follows:
//!
//! ```
//! use rspl::streams::infinite_lists::InfiniteList;
//! use rspl::streams::Stream;
//! use rspl::StreamProcessor;
//!
//! struct State {
//! toggle: bool,
//! }
//!
//! fn action(state: &mut State) {
//! state.toggle = !state.toggle;
//! }
//!
//! fn pre_action(state: State) -> State {
//! state
//! }
//!
//! fn post_action(state: State) -> State {
//! state
//! }
//!
//! fn generator_name<'a>(mut state: State) -> StreamProcessor<'a, (), bool> {
//! state = pre_action(state);
//! StreamProcessor::get(|_| {
//! action(&mut state);
//! StreamProcessor::put(state.toggle, || generator_name(post_action(state)))
//! })
//! }
//!
//! let generations = generator_name(State { toggle: false }).eval(InfiniteList::constant(()));
//!
//! assert!(generations.head());
//! ```
//!
//! A slightly more concrete example using that pattern is available as an [integration test](https://github.com/shtsoft/rspl/blob/master/tests/demands.rs).
//! A full-blown concrete example of a heat index control system can be found [here (as an .md file)](https://github.com/shtsoft/rspl/blob/master/examples/hics.md) and [here (as an .rs file)](https://github.com/shtsoft/rspl/blob/master/examples/hics.rs).
//!
//! [^1]: See [Codata in Action](https://www.microsoft.com/en-us/research/uploads/prod/2020/01/CoDataInAction.pdf) for more explanation of this term.
extern crate alloc;
use InfiniteList;
use Stream;
use Box;
/// A type for thunks of type `T`.
type Lazy<'a, T> = dyn FnOnce + 'a;
/// Terms of a language describing the domain of stream processors, that is, terms which can be interpreted to turn streams of type `A` into streams of type `B`.