extern crate alloc;
use alloc::boxed::Box;
use alloc::sync::Arc;
use core::future::Future;
use core::pin::Pin;
use super::eff::{Eff, run_purus};
use super::row_v2::EffectSet;
type BoxFutureSend<A> = Pin<Box<dyn Future<Output = A> + Send>>;
type EffRunner<E, A> = Box<dyn FnOnce(E) -> BoxFutureSend<A> + Send>;
#[inline]
pub async fn eff_to_async<A: Send + 'static>(eff: Eff<EffectSet<0>, A>) -> A {
run_purus(eff)
}
pub struct EffAsync<E, A> {
run: EffRunner<E, A>,
}
impl<E: 'static, A: 'static> EffAsync<E, A> {
#[inline]
pub fn new<F, Fut>(f: F) -> Self
where
F: FnOnce(E) -> Fut + Send + 'static,
Fut: Future<Output = A> + Send + 'static,
{
EffAsync {
run: Box::new(move |env| Box::pin(f(env))),
}
}
#[inline]
pub fn pure(value: A) -> Self
where
A: Send + 'static,
{
EffAsync {
run: Box::new(move |_| Box::pin(async move { value })),
}
}
#[inline]
pub async fn run(self, env: E) -> A {
(self.run)(env).await
}
#[inline]
pub fn map<B: 'static, F>(self, f: F) -> EffAsync<E, B>
where
F: FnOnce(A) -> B + Send + 'static,
E: Send + 'static,
A: Send,
{
EffAsync {
run: Box::new(move |env| {
Box::pin(async move {
let a = (self.run)(env).await;
f(a)
})
}),
}
}
#[inline]
pub fn flat_map<B: 'static, F>(self, f: F) -> EffAsync<E, B>
where
F: FnOnce(A) -> EffAsync<E, B> + Send + 'static,
E: Clone + Send + 'static,
A: Send,
{
EffAsync {
run: Box::new(move |env| {
let env2 = env.clone();
Box::pin(async move {
let a = (self.run)(env).await;
f(a).run(env2).await
})
}),
}
}
#[inline]
pub fn ask() -> EffAsync<E, E>
where
E: Clone + Send + 'static,
{
EffAsync::new(|env: E| async move { env })
}
#[inline]
pub fn local<F>(f: F, inner: EffAsync<E, A>) -> EffAsync<E, A>
where
F: FnOnce(E) -> E + Send + 'static,
E: Send + 'static,
A: Send,
{
EffAsync {
run: Box::new(move |env| {
let modified_env = f(env);
(inner.run)(modified_env)
}),
}
}
}
#[inline]
pub fn lift_eff<E: 'static, A: Send + 'static>(eff: Eff<EffectSet<0>, A>) -> EffAsync<E, A> {
EffAsync::new(move |_| {
let result = run_purus(eff);
async move { result }
})
}
#[inline]
pub fn lift_async<E: 'static, A: 'static, Fut>(fut: Fut) -> EffAsync<E, A>
where
Fut: Future<Output = A> + Send + 'static,
{
EffAsync {
run: Box::new(move |_| Box::pin(fut)),
}
}
use crate::transformers::async_transforms::LectorAsync;
#[inline]
pub fn eff_to_lector<E: Clone + Send + Sync + 'static, A: Clone + Send + Sync + 'static>(
eff: Eff<EffectSet<0>, A>,
) -> LectorAsync<E, A> {
let result = run_purus(eff);
LectorAsync::purus(result)
}
#[inline]
pub fn lector_to_eff_async<E: Clone + Send + Sync + 'static, A: Send + 'static>(
lector: LectorAsync<E, A>,
) -> EffAsync<E, A>
where
LectorAsync<E, A>: Clone,
{
EffAsync::new(move |env: E| {
let lector = lector.clone();
async move { lector.run(env).await }
})
}
pub fn sequence_eff_async<E, A, B>(
first: EffAsync<E, A>,
second: EffAsync<E, B>,
) -> EffAsync<E, (A, B)>
where
E: Clone + Send + 'static,
A: Send + 'static,
B: Send + 'static,
{
first.flat_map(move |a| second.map(move |b| (a, b)))
}
pub fn traverse_eff_async<E, A, B, I, F>(items: I, f: F) -> EffAsync<E, alloc::vec::Vec<B>>
where
E: Clone + Send + 'static,
A: Send + 'static,
B: Send + 'static,
I: IntoIterator<Item = A>,
F: Fn(A) -> EffAsync<E, B> + Send + Sync + 'static,
{
let items: alloc::vec::Vec<_> = items.into_iter().collect();
let f = Arc::new(f);
EffAsync::new(move |env: E| {
let f = Arc::clone(&f);
async move {
let mut results = alloc::vec::Vec::with_capacity(items.len());
for item in items {
let eff = f(item);
let result = eff.run(env.clone()).await;
results.push(result);
}
results
}
})
}
#[cfg(all(test, feature = "tokio"))]
mod tests {
use super::*;
use alloc::string::{String, ToString};
#[test]
fn test_eff_async_pure() {
let eff: EffAsync<(), i32> = EffAsync::pure(42);
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.expect("tokio current-thread runtime should build successfully");
let result = rt.block_on(eff.run(()));
assert_eq!(result, 42);
}
#[test]
fn test_eff_async_map() {
let eff: EffAsync<(), i32> = EffAsync::pure(21);
let doubled = eff.map(|x| x * 2);
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.expect("tokio current-thread runtime should build successfully");
let result = rt.block_on(doubled.run(()));
assert_eq!(result, 42);
}
#[test]
fn test_eff_async_ask() {
let ask_eff: EffAsync<String, String> = EffAsync::<String, String>::ask();
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.expect("tokio current-thread runtime should build successfully");
let result = rt.block_on(ask_eff.run("hello".to_string()));
assert_eq!(result, "hello");
}
#[test]
fn test_eff_async_flat_map() {
let eff: EffAsync<i32, i32> = EffAsync::<i32, i32>::ask();
let result = eff.flat_map(|env| EffAsync::pure(env * 2));
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.expect("tokio current-thread runtime should build successfully");
let value = rt.block_on(result.run(21));
assert_eq!(value, 42);
}
#[test]
fn test_lift_eff_to_eff_async() {
let eff: Eff<EffectSet<0>, i32> = Eff::purus(42);
let eff_async: EffAsync<(), i32> = lift_eff(eff);
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.expect("tokio current-thread runtime should build successfully");
let result = rt.block_on(eff_async.run(()));
assert_eq!(result, 42);
}
#[test]
fn test_eff_to_lector() {
let eff: Eff<EffectSet<0>, i32> = Eff::purus(42);
let lector: LectorAsync<String, i32> = eff_to_lector(eff);
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.expect("tokio current-thread runtime should build successfully");
let result = rt.block_on(lector.run("ignored".to_string()));
assert_eq!(result, 42);
}
#[test]
fn test_eff_async_local() {
let inner: EffAsync<i32, i32> = EffAsync::<i32, i32>::ask();
let with_local = EffAsync::local(|x: i32| x * 2, inner);
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.expect("tokio current-thread runtime should build successfully");
let result = rt.block_on(with_local.run(21));
assert_eq!(result, 42);
}
#[test]
fn test_sequence_eff_async() {
let first: EffAsync<(), i32> = EffAsync::pure(1);
let second: EffAsync<(), i32> = EffAsync::pure(2);
let combined = sequence_eff_async(first, second);
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.expect("tokio current-thread runtime should build successfully");
let (a, b) = rt.block_on(combined.run(()));
assert_eq!(a, 1);
assert_eq!(b, 2);
}
}