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
#![feature(generator_trait, never_type, core_intrinsics, extern_types)]

use std::marker::PhantomData;
use std::pin::Pin;
use std::thread;

pub use eff_attr::eff;
#[doc(hidden)]
pub use pin_utils::pin_mut;
#[doc(hidden)]
pub use std::pin as pin_reexport;

pub mod context;
pub mod coproduct;
pub mod either;
pub mod embed;
pub mod generator;
pub mod handled;
pub mod lazy;

pub use context::{Context, TypedContext};
pub use generator::from_generator;
pub use lazy::{lazy, pure};

use either::Either;
use embed::EmbedEffect;
use handled::{Handled, HandlerArgument};

/// A coproduct type of effects
#[macro_export]
macro_rules! Coproduct {
    () => {
        !
    };
    ($head:ty $(,$tail:ty)* $(,)?) => {
        $crate::coproduct::Either<$head, $crate::Coproduct![$($tail),*]>
    };
}

/// Performs an effect, suspending the current computation until the task gets waken
#[macro_export]
macro_rules! perform {
    ($eff:expr) => {{
        match $eff {
            eff => {
                let taker = $crate::context::gen_taker(&eff);
                let cx = $crate::context::get_task_context();
                yield $crate::Suspension::Effect($crate::coproduct::Inject::inject(
                    eff,
                    cx.typed(),
                ));
                loop {
                    if let Some(v) = taker(&$crate::context::get_task_context()) {
                        break v;
                    } else {
                        yield $crate::Suspension::Pending;
                    }
                }
            }
        }
    }};
}

/// Runs an effectful computation under the current context
///
/// When the computation performs an effect, this computation re-performs it as is
#[macro_export]
macro_rules! perform_from {
    ($eff:expr) => {{
        match $eff {
            eff => {
                $crate::pin_mut!(eff);
                let cx = $crate::context::get_task_context();
                loop {
                    let eff = $crate::pin_reexport::Pin::as_mut(&mut eff);
                    match $crate::Effectful::poll(eff, cx) {
                        $crate::Poll::Done(x) => break x,
                        $crate::Poll::Effect(e) => {
                            yield $crate::Suspension::Effect($crate::coproduct::Embed::embed(e));
                        }
                        $crate::Poll::Pending => {
                            yield $crate::Suspension::Pending;
                        }
                    }
                }
            }
        }
    }};
}

#[doc(hidden)]
#[macro_export]
macro_rules! handler_impl {
    ($e:expr , ) => {{
        let e: ! = $e;
        e
    }};
    ($e:expr , $effect:pat, $k:pat => $handler:expr; $($effects:pat, $ks:pat => $handlers:expr;)*) => {{
        match $e {
            $crate::coproduct::Either::A($effect, $k) => $handler,
            $crate::coproduct::Either::B(effect) => $crate::handler_impl!(effect , $($effects, $ks => $handlers;)*),
        }
    }};
}

/// Create a handler
///
/// The first arm corresponds to a value handler which is called upon completing the source computation.
///
/// The other arms handles effects performed by the source computation. Each arm takes an effect
/// and a continuation which can wake up the task via `waker()`. See also
/// [TypedContext](context/struct.TypedContext.html).
#[macro_export]
macro_rules! handler {
    ($value:pat => $value_handler:expr $(, $effect:pat, $k:pat => $handler:expr)* $(,)?) => {{
        #[allow(unreachable_code)]
        |arg| $crate::from_generator(static move || {
            if false {
                yield unreachable!();
            }
            match arg {
                $crate::handled::HandlerArgument::Done(x) => match x {
                    $value => $value_handler,
                },
                $crate::handled::HandlerArgument::Effect(e) => $crate::handler_impl!(e , $($effect, $k => $handler;)*),
            }
        })
    }};
}

/// An effectful computation block
///
/// The block must contain `perform!` or `perform_from!`
#[macro_export]
macro_rules! effectful {
    ($($tts:tt)*) => {{
        $crate::from_generator(static move || {
            $($tts)*
        })
    }};
}

/// A computational effect that will be resolved to `Output`
pub trait Effect {
    type Output;
}

/// A special effect representing the continuation of the source computation in handlers
#[derive(Debug)]
pub struct Continue<R>(PhantomData<R>);

impl<R> Effect for Continue<R> {
    type Output = R;
}

impl<R> Continue<R> {
    fn new() -> Self {
        Continue(PhantomData)
    }
}

/// The state of an effectful computation
pub enum Poll<T, Effect> {
    /// The computation is done
    Done(T),
    /// An effect has been performed
    Effect(Effect),
    /// The computation is not ready to continue
    Pending,
}

/// The cause for suspension of the computation
pub enum Suspension<Effect> {
    Effect(Effect),
    Pending,
}

/// An effectful computation
pub trait Effectful {
    /// The type of the final result
    type Output;

    /// The type of the effects this computation will produce
    type Effect;

    /// Takes a value handler and an effect handler and creates an effectful computation with
    /// the effects handled
    #[inline]
    fn handle<H, HC, Effect, I>(self, handler: H) -> Handled<Self, H, HC, Effect, I>
    where
        Self: Sized,
        H: FnMut(HandlerArgument<Self::Output, Effect>) -> HC,
        Self::Effect: coproduct::Subset<Effect, I>,
    {
        Handled::new(self, handler)
    }

    /// Creates an effectful computation whose effect is a superset of that of this one
    #[inline]
    fn embed<Target, Indices>(self) -> EmbedEffect<Self, Target, Indices>
    where
        Self: Sized,
    {
        EmbedEffect::new(self)
    }

    /// Combine this and the other computation with the same signature
    #[inline]
    fn left<R>(self) -> Either<Self, R>
    where
        Self: Sized,
    {
        Either::A(self)
    }

    /// Combine this and the other computation with the same signature
    #[inline]
    fn right<L>(self) -> Either<L, Self>
    where
        Self: Sized,
    {
        Either::B(self)
    }

    /// Create a boxed computation
    ///
    /// This function can be used to erase `Self` type
    #[inline]
    fn boxed<'a>(self) -> Pin<Box<dyn Effectful<Output = Self::Output, Effect = Self::Effect> + 'a>>
    where
        Self: Sized + 'a,
    {
        Box::pin(self)
    }

    /// Run the computation to completion on the current thread
    ///
    /// This method blocks the current thread while waiting on the progress of the computation
    ///
    /// The effect type of this computation must be an empty set (never type) since there is no handler
    #[inline]
    fn block_on(self) -> Self::Output
    where
        Self: Sized + Effectful<Effect = !>,
    {
        use Poll::*;

        let this = self;
        pin_mut!(this);

        let cx = Context::current();

        loop {
            match this.as_mut().poll(&cx) {
                Done(v) => return v,
                Effect(e) => e,            // unreachable
                Pending => thread::park(), // park until wake
            }
        }
    }

    /// Resume the computation to a final value, registering the current task
    /// for wakeup if a handler starts handling an effect performed by the task
    ///
    /// # Return value
    /// This function returns:
    /// - `Poll::Done(v)` with a result `v` if the computation completed successfully
    /// - `Poll::Effect(e)` with an effect `e` if the computation performed a computational effect
    /// - `Poll::Pending` if the computation is not ready to continue because a handler is handling an effect
    ///
    /// Once a computation has completed, clients should not `poll` it again
    ///
    /// When a computation performs an effect, `poll` returns `Poll::Effect(e)` with the effect `e`
    ///
    /// When a handler decides to handle an effect, it will register interest in the result for the
    /// current task. In this case, `poll` returns `Poll::NotReady` until the task gets woken up
    /// with the outcome of the effect.
    ///
    /// # Panics
    /// After the completion of the computation (`poll` returned `Poll::Done`), future calls to `poll`
    /// may panic or cause bad behavior. The `Effectful` trait does not provide any guarantees
    /// about the safety of calling `poll` after the task has finished.
    fn poll(self: Pin<&mut Self>, cx: &Context) -> Poll<Self::Output, Self::Effect>;
}

impl<C> Effectful for Pin<&'_ mut C>
where
    C: Effectful + ?Sized,
{
    type Output = C::Output;
    type Effect = C::Effect;

    fn poll(mut self: Pin<&mut Self>, cx: &Context) -> Poll<Self::Output, Self::Effect> {
        C::poll((*self).as_mut(), cx)
    }
}

impl<C> Effectful for Pin<Box<C>>
where
    C: Effectful + ?Sized,
{
    type Output = C::Output;
    type Effect = C::Effect;

    fn poll(mut self: Pin<&mut Self>, cx: &Context) -> Poll<Self::Output, Self::Effect> {
        C::poll((*self).as_mut(), cx)
    }
}