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
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
//! This library brings typed effects to Rust in a flexible and composable way. By building on
//! [`Generator`]s, effectful computations can be expressed in a way that allows arbitrary and
//! swappable behaviour - any handler of the correct type can be applied to a computation, meaning
//! different semantics of effects can be selected at each call site of an effectful function.
//!
//! ## Glossary
//! - effectful computation: an in-progress computation that uses effects. analogous to `Future`.
//! - effectful function: a function that returns an effectful computation. analogous to `async fn`.
//! - effect: you know, I'm actually not sure. I should ask one of my PL teachers. In this library
//! though, an effect is a value that can be passed out of an effectful computation and into an
//! effect handler, which produces another value to pass back in.
//! - injection: an `effing_mad` term referring to the value passed into a computation as a result of
//! it running an effect.
//! - "pure" function: a Rust function that does not use `effing_mad` effects. Rust is not a pure
//! language (crudely, Rust code can `println!()` whenever it wants) so these docs use quotes to
//! indicate this meaning as opposed to the real meaning of pure, where functions do not use side
//! effects.
//!
//! ## Getting started
//! Define an [`Effect`]. Now, you can define an [`#[effectful(…)]`](effing_macros::effectful)
//! function that uses it. Once you call this function, its effects can be handled one by one with
//! [`handle`]. Handlers are "pure" Rust functions, but it's easiest to construct them using
//! [`handler!`](effing_macros::handler). Once all the effects have been handled away, a computation
//! can be driven with [`run`].
//!
//! ## Interaction with `async`
//! There are two ways to bring together the async world and the effectful world. The first, and
//! simplest, is [`handle_async`]. This allows you to handle the last effect in a computation using
//! a handler that is an `async fn`.
//!
//! The second, more freaky way is with the contents of [`effects::future`]. These allow you to
//! convert between futures and effectful computations freely - namely the `effectfulise` function
//! and the `futurise` function will take your futures and your computations and abstract away all
//! the other nonsense in that module to get you the respective constructs.

#![feature(doc_auto_cfg)]
#![feature(doc_notable_trait)]
#![feature(generators)]
#![feature(generator_trait)]
#![no_std]
#![warn(missing_docs)]

#[cfg(feature = "alloc")]
extern crate alloc;

pub use frunk;

pub mod effects;
pub mod higher_order;
pub mod injection;
pub mod macro_impl;

use core::{
    future::Future,
    ops::{ControlFlow, Generator, GeneratorState},
    pin::Pin,
};
use frunk::{
    coproduct::{CNil, CoprodInjector, CoprodUninjector, CoproductEmbedder, CoproductSubsetter},
    Coprod, Coproduct,
};

pub use effing_macros::{effectful, effects, handler};
use injection::{Begin, EffectList, Tagged};

/// An uninhabited type that can never be constructed.
///
/// Substitutes for `!` until that is stabilised.
pub enum Never {}

/// Run an effectful computation that has no effects.
///
/// Effectful computations are generators, but if they have no effects, it is guaranteed that they
/// will never yield. Therefore they can be run by resuming them once. This function does that.
pub fn run<F, R>(mut f: F) -> R
where
    F: Generator<Coproduct<Begin, CNil>, Yield = CNil, Return = R>,
{
    let pinned = core::pin::pin!(f);
    match pinned.resume(Coproduct::Inl(Begin)) {
        GeneratorState::Yielded(never) => match never {},
        GeneratorState::Complete(ret) => ret,
    }
}

/// An effect that must be handled by the caller of an effectful computation, or propagated up the
/// call stack.
pub trait Effect {
    /// The type of value that running this effect gives.
    type Injection;
}

/// Types which represent multiple effects.
///
/// Effects that are commonly used together can be grouped using a type that implements this trait.
/// Defining an [`#[effectful]`](effing_macros::effectful) function that uses all of the effects in
/// a group can then be done by naming the group instead of each effect.
#[doc(notable_trait)]
pub trait EffectGroup {
    /// A [`Coproduct`](frunk::coproduct::Coproduct) of effects in this group.
    type Effects;
}

impl<E: Effect> EffectGroup for E {
    type Effects = Coproduct<E, CNil>;
}

/// Create a new effectful computation by applying a "pure" function to the return value of an
/// existing computation.
pub fn map<E, I, T, U>(
    mut g: impl Generator<I, Yield = E, Return = T>,
    f: impl FnOnce(T) -> U,
) -> impl Generator<I, Yield = E, Return = U> {
    move |mut injs: I| {
        loop {
            // safety: see handle_group()
            let pinned = unsafe { Pin::new_unchecked(&mut g) };
            match pinned.resume(injs) {
                GeneratorState::Yielded(effs) => injs = yield effs,
                GeneratorState::Complete(ret) => return f(ret),
            }
        }
    }
}

/// Apply a "pure" handler to an effectful computation, handling one effect.
///
/// When given an effectful computation with effects (A, B, C) and a handler for effect C, this
/// returns a new effectful computation with effects (A, B). Handlers can choose for each instance
/// of their effect whether to resume the computation, passing in a value (injection) or to force a
/// return from the computation. This is done using
/// [`ControlFlow::Continue`](core::ops::ControlFlow::Continue) and
/// [`ControlFlow::Break`](core::ops::ControlFlow::Break) respectively.
///
/// For handling multiple effects with one closure, see [`handle_group`].
pub fn handle<
    G,
    R,
    E,
    PreEs,
    PostEs,
    EffIndex,
    PreIs,
    PostIs,
    BeginIndex,
    InjIndex,
    InjsIndices,
    EmbedIndices,
>(
    g: G,
    mut handler: impl FnMut(E) -> ControlFlow<R, E::Injection>,
) -> impl Generator<PostIs, Yield = PostEs, Return = R>
where
    E: Effect,
    Coprod!(Tagged<E::Injection, E>, Begin): CoproductEmbedder<PreIs, InjsIndices>,
    PreEs: EffectList<Injections = PreIs> + CoprodUninjector<E, EffIndex, Remainder = PostEs>,
    PostEs: EffectList<Injections = PostIs>,
    PreIs: CoprodInjector<Begin, BeginIndex> + CoprodInjector<Tagged<E::Injection, E>, InjIndex>,
    PostIs: CoproductEmbedder<PreIs, EmbedIndices>,
    G: Generator<PreIs, Yield = PreEs, Return = R>,
{
    handle_group(g, move |effs| match effs {
        Coproduct::Inl(eff) => match handler(eff) {
            ControlFlow::Continue(inj) => ControlFlow::Continue(Coproduct::Inl(Tagged::new(inj))),
            ControlFlow::Break(ret) => ControlFlow::Break(ret),
        },
        Coproduct::Inr(never) => match never {},
    })
}

/// Apply a "pure" handler to an effectful computation, handling any number of effects.
///
/// When given an effectful computation with effects (A, B, C, D) and a handler for effects (A, B),
/// this function returns a new effectful computation with effects (C, D). Handlers can choose for
/// each instance of their effects whether to resume the computation, passing in a value (injection)
/// or to force a return from the computation. This is done using
/// [`ControlFlow::Continue`](core::ops::ControlFlow::Continue) and
/// [`ControlFlow::Break`](core::ops::ControlFlow::Break) respectively.
///
/// `Es` must be a [`Coproduct`](frunk::Coproduct) of effects.
///
/// Care should be taken to only produce an injection type when handling the corresponding effect.
/// If the injection type does not match the effect that is being handled, the computation will
/// most likely panic.
pub fn handle_group<
    G,
    R,
    Es,
    Is,
    PreEs,
    PostEs,
    PreIs,
    PostIs,
    EffsIndices,
    InjsIndices,
    BeginIndex,
    EmbedIndices,
>(
    mut g: G,
    mut handler: impl FnMut(Es) -> ControlFlow<R, Is>,
) -> impl Generator<PostIs, Yield = PostEs, Return = R>
where
    Es: EffectList<Injections = Is>,
    Is: CoproductEmbedder<PreIs, InjsIndices>,
    PreEs: EffectList<Injections = PreIs> + CoproductSubsetter<Es, EffsIndices, Remainder = PostEs>,
    PostEs: EffectList<Injections = PostIs>,
    PreIs: CoprodInjector<Begin, BeginIndex>,
    PostIs: CoproductEmbedder<PreIs, EmbedIndices>,
    G: Generator<PreIs, Yield = PreEs, Return = R>,
{
    move |_begin: PostIs| {
        let mut injection = PreIs::inject(Begin);
        loop {
            // safety: im 90% sure that since we are inside Generator::resume which takes
            // Pin<&mut self>, all locals in this function are effectively pinned and this call is
            // simply projecting that
            let pinned = unsafe { Pin::new_unchecked(&mut g) };
            match pinned.resume(injection) {
                GeneratorState::Yielded(effs) => match effs.subset() {
                    Ok(effs) => match handler(effs) {
                        ControlFlow::Continue(injs) => injection = injs.embed(),
                        ControlFlow::Break(ret) => return ret,
                    },
                    Err(effs) => injection = (yield effs).embed(),
                },
                GeneratorState::Complete(ret) => return ret,
            }
        }
    }
}

/// Handle the last effect in a computation using an async handler.
///
/// For handling multiple effects asynchronously, see [`handle_group_async`]. For details on
/// handling effects, see [`handle`].
///
/// When an async handler is used, it must handle all of the remaining effects in a computation,
/// because it is impossible to construct a computation that is both asynchronous and effectful.
///
/// For more flexible interactions with Futures, see [`effects::future`].
pub async fn handle_async<Eff, G, Fut>(mut g: G, mut handler: impl FnMut(Eff) -> Fut) -> G::Return
where
    Eff: Effect,
    G: Generator<Coprod!(Tagged<Eff::Injection, Eff>, Begin), Yield = Coprod!(Eff)>,
    Fut: Future<Output = ControlFlow<G::Return, Eff::Injection>>,
{
    let mut injs = Coproduct::inject(Begin);
    loop {
        // safety: see handle_group() - remember that futures are pinned in the same way as
        // generators
        let pinned = unsafe { Pin::new_unchecked(&mut g) };
        match pinned.resume(injs) {
            GeneratorState::Yielded(effs) => {
                let eff = match effs {
                    Coproduct::Inl(v) => v,
                    Coproduct::Inr(never) => match never {},
                };
                match handler(eff).await {
                    ControlFlow::Continue(new_injs) => injs = Coproduct::Inl(Tagged::new(new_injs)),
                    ControlFlow::Break(ret) => return ret,
                }
            },
            GeneratorState::Complete(ret) => return ret,
        }
    }
}

/// Handle all of the remaining effects in a computation using an async handler.
///
/// For handling one effect asynchronously, see [`handle_async`]. For details on handling effects in
/// groups, see [`handle_group`].
///
/// When an async handler is used, it must handle all of the remaining effects in a computation,
/// because it is impossible to construct a computation that is both asynchronous and effectful.
///
/// For more flexible interactions with Futures, see [`effects::future`].
pub async fn handle_group_async<G, Fut, Es, Is, BeginIndex>(
    mut g: G,
    mut handler: impl FnMut(Es) -> Fut,
) -> G::Return
where
    Es: EffectList<Injections = Is>,
    Is: CoprodInjector<Begin, BeginIndex>,
    G: Generator<Is, Yield = Es>,
    Fut: Future<Output = ControlFlow<G::Return, Is>>,
{
    let mut injs = Is::inject(Begin);
    loop {
        // safety: see handle_group() - remember that futures are pinned in the same way as
        // generators
        let pinned = unsafe { Pin::new_unchecked(&mut g) };
        match pinned.resume(injs) {
            GeneratorState::Yielded(effs) => match handler(effs).await {
                ControlFlow::Continue(new_injs) => injs = new_injs,
                ControlFlow::Break(ret) => return ret,
            },
            GeneratorState::Complete(ret) => return ret,
        }
    }
}

/// Handle one effect in the computation `g` by running other effects.
///
/// It is not possible to get rustc to infer the type of `PostEs`, so calling this function almost
/// always requires annotating that - which means you also have to annotate 21 underscores.
/// For this reason, prefer to use [`transform0`] or [`transform1`] instead, which should not
/// require annotation.
pub fn transform<
    G1,
    R,
    E,
    H,
    PreEs,
    PreHandleEs,
    HandlerEs,
    PostEs,
    EffIndex,
    PreIs,
    PreHandleIs,
    HandlerIs,
    PostIs,
    BeginIndex1,
    BeginIndex2,
    BeginIndex3,
    InjIndex,
    SubsetIndices1,
    SubsetIndices2,
    EmbedIndices1,
    EmbedIndices2,
    EmbedIndices3,
>(
    mut g: G1,
    mut handler: impl FnMut(E) -> H,
) -> impl Generator<PostIs, Yield = PostEs, Return = R>
where
    E: Effect,
    H: Generator<HandlerIs, Yield = HandlerEs, Return = E::Injection>,
    PreEs: EffectList<Injections = PreIs> + CoprodUninjector<E, EffIndex, Remainder = PreHandleEs>,
    PreHandleEs: EffectList<Injections = PreHandleIs> + CoproductEmbedder<PostEs, EmbedIndices1>,
    HandlerEs: EffectList<Injections = HandlerIs> + CoproductEmbedder<PostEs, EmbedIndices2>,
    PostEs: EffectList<Injections = PostIs>,
    PreIs: CoprodInjector<Begin, BeginIndex1>
        + CoprodUninjector<Tagged<E::Injection, E>, InjIndex, Remainder = PreHandleIs>,
    PreHandleIs: CoproductEmbedder<PreIs, EmbedIndices3>,
    HandlerIs: CoprodInjector<Begin, BeginIndex2>,
    PostIs: CoprodInjector<Begin, BeginIndex3>
        + CoproductSubsetter<
            <PreIs as CoprodUninjector<Tagged<E::Injection, E>, InjIndex>>::Remainder,
            SubsetIndices1,
        > + CoproductSubsetter<HandlerIs, SubsetIndices2>,
    G1: Generator<PreIs, Yield = PreEs, Return = R>,
{
    move |_begin: PostIs| {
        let mut injection = PreIs::inject(Begin);
        loop {
            // safety: see handle_group()
            let pinned = unsafe { Pin::new_unchecked(&mut g) };
            match pinned.resume(injection) {
                GeneratorState::Yielded(effs) => match effs.uninject() {
                    // the effect we are handling
                    Ok(eff) => {
                        let mut handling = handler(eff);
                        let mut handler_inj = HandlerIs::inject(Begin);
                        'run_handler: loop {
                            // safety: same again
                            let pinned = unsafe { Pin::new_unchecked(&mut handling) };
                            match pinned.resume(handler_inj) {
                                GeneratorState::Yielded(effs) => {
                                    handler_inj = PostIs::subset(yield effs.embed()).ok().unwrap();
                                },
                                GeneratorState::Complete(inj) => {
                                    injection = PreIs::inject(Tagged::new(inj));
                                    break 'run_handler;
                                },
                            }
                        }
                    },
                    // any other effect
                    Err(effs) => {
                        injection =
                            PreHandleIs::embed(PostIs::subset(yield effs.embed()).ok().unwrap());
                    },
                },
                GeneratorState::Complete(ret) => return ret,
            }
        }
    }
}

/// Handle one effect of `g` by running other effects that it already uses.
///
/// This function is a special case of [`transform`] for when the handler does not introduce any
/// effects on top of the ones from `g` that it's not handling.
///
/// For introducing a new effect, see [`transform1`].
pub fn transform0<
    G1,
    R,
    E,
    H,
    PreEs,
    HandlerEs,
    PostEs,
    EffIndex,
    PreIs,
    HandlerIs,
    PostIs,
    I1Index,
    BeginIndex1,
    BeginIndex2,
    BeginIndex3,
    SubsetIndices1,
    SubsetIndices2,
    EmbedIndices1,
    EmbedIndices2,
    EmbedIndices3,
>(
    g: G1,
    handler: impl FnMut(E) -> H,
) -> impl Generator<PostIs, Yield = PostEs, Return = R>
where
    E: Effect,
    H: Generator<HandlerIs, Yield = HandlerEs, Return = E::Injection>,
    PreEs: EffectList<Injections = PreIs> + CoprodUninjector<E, EffIndex, Remainder = PostEs>,
    HandlerEs: EffectList<Injections = HandlerIs> + CoproductEmbedder<PostEs, EmbedIndices1>,
    PostEs: EffectList<Injections = PostIs> + CoproductEmbedder<PostEs, EmbedIndices2>,
    PreIs: CoprodInjector<Begin, BeginIndex1>
        + CoprodUninjector<Tagged<E::Injection, E>, I1Index, Remainder = PostIs>,
    HandlerIs: CoprodInjector<Begin, BeginIndex2>,
    PostIs: CoprodInjector<Begin, BeginIndex3>
        + CoproductSubsetter<HandlerIs, SubsetIndices1>
        + CoproductSubsetter<PostIs, SubsetIndices2>
        + CoproductEmbedder<PreIs, EmbedIndices3>,
    G1: Generator<PreIs, Yield = PreEs, Return = R>,
{
    transform(g, handler)
}

/// Handle one effect of `g` by running a new effect.
///
/// This function is a special case of [`transform`] for when the handler introduces one effect on
/// top of the ones from `g` that it's not handling.
///
/// It is possible for the handler to run effects from `g` as well as the effect that it introduces.
///
/// To transform without introducing any effects, see [`transform0`].
pub fn transform1<
    G1,
    R,
    E1,
    E2,
    H,
    PreEs,
    PreHandleEs,
    HandlerEs,
    E1Index,
    PreIs,
    PreHandleIs,
    HandlerIs,
    I1Index,
    BeginIndex1,
    BeginIndex2,
    BeginIndex3,
    SubsetIndices1,
    SubsetIndices2,
    EmbedIndices1,
    EmbedIndices2,
    EmbedIndices3,
>(
    g: G1,
    handler: impl FnMut(E1) -> H,
) -> impl Generator<
    Coproduct<Tagged<E2::Injection, E2>, PreHandleIs>,
    Yield = Coproduct<E2, PreHandleEs>,
    Return = R,
>
where
    E1: Effect,
    E2: Effect,
    H: Generator<HandlerIs, Yield = HandlerEs, Return = E1::Injection>,
    PreEs: EffectList<Injections = PreIs> + CoprodUninjector<E1, E1Index, Remainder = PreHandleEs>,
    PreHandleEs: EffectList<Injections = PreHandleIs>
        + CoproductEmbedder<Coproduct<E2, PreHandleEs>, EmbedIndices1>,
    HandlerEs: EffectList<Injections = HandlerIs>
        + CoproductEmbedder<Coproduct<E2, PreHandleEs>, EmbedIndices2>,
    PreIs: CoprodInjector<Begin, BeginIndex1>
        + CoprodUninjector<Tagged<E1::Injection, E1>, I1Index, Remainder = PreHandleIs>,
    PreHandleIs: CoproductEmbedder<PreIs, EmbedIndices3>,
    HandlerIs: CoprodInjector<Begin, BeginIndex2>,
    Coproduct<Tagged<E2::Injection, E2>, PreHandleIs>: CoprodInjector<Begin, BeginIndex3>
        + CoproductSubsetter<HandlerIs, SubsetIndices1>
        + CoproductSubsetter<PreHandleIs, SubsetIndices2>,
    G1: Generator<PreIs, Yield = PreEs, Return = R>,
{
    transform(g, handler)
}