use std::future::IntoFuture;
use futures::{executor::LocalPool, future::LocalBoxFuture, Future};
use crate::{Apply, Bind, Functor, Pure};
pub struct Effect<'a, A> {
future: LocalBoxFuture<'a, A>,
}
impl<'a, A> core::fmt::Debug for Effect<'a, A> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(&format!("Effect<{}>", std::any::type_name::<A>()))
}
}
impl<'a, A> IntoFuture for Effect<'a, A> {
type Output = A;
type IntoFuture = LocalBoxFuture<'a, A>;
fn into_future(self) -> Self::IntoFuture {
self.future
}
}
impl<'a, A, F> From<F> for Effect<'a, A>
where
F: Future<Output = A> + 'a,
{
fn from(future: F) -> Self {
Self {
future: Box::pin(future),
}
}
}
impl<'a, A> Bind<'a, A> for Effect<'a, A>
where
A: 'a,
{
type Target<T> = Effect<'a, T>;
fn bind<B, F>(self, f: F) -> Self::Target<B>
where
F: Fn(A) -> Self::Target<B> + 'a,
{
async move { f(self.await).await }.into()
}
}
impl<'a, A> Functor<'a, A> for Effect<'a, A>
where
A: 'a,
{
type Target<T> = Effect<'a, T>;
fn fmap<B, F>(self, f: F) -> Self::Target<B>
where
F: Fn(A) -> B + 'a,
{
async move { f(self.await) }.into()
}
}
impl<'a, A> Pure<A> for Effect<'a, A>
where
A: 'a,
{
fn pure(value: A) -> Self {
async move { value }.into()
}
}
impl<'a, A> Apply<'a, A> for Effect<'a, A>
where
A: 'a,
{
type Target<T> = Effect<'a, T> where T:'a;
fn apply<B>(
self,
f: <Self as Apply<'a, A>>::Target<crate::apply::ApplyFn<'a, A, B>>,
) -> <Self as Apply<'a, A>>::Target<B>
where
B: 'a,
{
async move {
let func = f.await;
let arg = self.await;
func.apply(arg)
}
.into()
}
}
pub fn run_effect<'a, A>(effect: Effect<'a, A>) -> A {
LocalPool::new().run_until(effect.into_future())
}