use alloc::sync::Arc;
use core::marker::PhantomData;
pub(crate) type ContFn<R, A> = dyn Fn(Arc<dyn Fn(A) -> R + Send + Sync>) -> R + Send + Sync;
pub struct ContinuatioT<R, A> {
run_cont: Arc<ContFn<R, A>>,
_phantom: PhantomData<(R, A)>,
}
impl<R, A> Clone for ContinuatioT<R, A> {
fn clone(&self) -> Self {
ContinuatioT {
run_cont: self.run_cont.clone(),
_phantom: PhantomData,
}
}
}
impl<R: 'static, A: 'static> ContinuatioT<R, A> {
#[inline]
pub fn new<F>(f: F) -> Self
where
F: Fn(Arc<dyn Fn(A) -> R + Send + Sync>) -> R + Send + Sync + 'static,
{
ContinuatioT {
run_cont: Arc::new(f),
_phantom: PhantomData,
}
}
#[inline]
pub fn exsequi<F>(&self, k: F) -> R
where
F: Fn(A) -> R + Send + Sync + 'static,
{
(self.run_cont)(Arc::new(k))
}
#[inline]
pub fn run<F>(&self, k: F) -> R
where
F: Fn(A) -> R + Send + Sync + 'static,
{
self.exsequi(k)
}
#[inline]
pub fn pure(a: A) -> Self
where
A: Clone + Send + Sync + 'static,
{
ContinuatioT::new(move |k| k(a.clone()))
}
#[inline]
pub fn map<B, F>(self, f: F) -> ContinuatioT<R, B>
where
F: Fn(A) -> B + Send + Sync + 'static,
B: 'static,
{
let f = Arc::new(f);
let run_cont = self.run_cont;
ContinuatioT::new(move |k| {
let f = f.clone();
run_cont(Arc::new(move |a| k(f(a))))
})
}
#[inline]
pub fn bind<B, F>(self, f: F) -> ContinuatioT<R, B>
where
F: Fn(A) -> ContinuatioT<R, B> + Send + Sync + 'static,
B: 'static,
{
let f = Arc::new(f);
let run_cont = self.run_cont;
ContinuatioT::new(move |k| {
let f = f.clone();
let k = k.clone();
run_cont(Arc::new(move |a| {
let cont_b = f(a);
(cont_b.run_cont)(k.clone())
}))
})
}
#[inline]
pub fn flat_map<B, F>(self, f: F) -> ContinuatioT<R, B>
where
F: Fn(A) -> ContinuatioT<R, B> + Send + Sync + 'static,
B: 'static,
{
self.bind(f)
}
#[inline]
pub fn call_cc<B, F>(f: F) -> ContinuatioT<R, A>
where
F: Fn(Arc<dyn Fn(A) -> ContinuatioT<R, B> + Send + Sync>) -> ContinuatioT<R, A>
+ Send
+ Sync
+ 'static,
A: Clone + Send + Sync + 'static,
B: 'static,
{
ContinuatioT::new(move |k| {
let k_clone = k.clone();
let escape = Arc::new(move |a: A| {
let k_inner = k_clone.clone();
ContinuatioT::<R, B>::new(move |_ignored| k_inner(a.clone()))
});
(f(escape).run_cont)(k)
})
}
}
impl<R: 'static, A: 'static> ContinuatioT<R, A> {
#[inline]
pub fn apply<B>(
self,
cf: ContinuatioT<R, Arc<dyn Fn(A) -> B + Send + Sync>>,
) -> ContinuatioT<R, B>
where
B: 'static,
{
let run_val = self.run_cont;
let run_fn = cf.run_cont;
ContinuatioT::new(move |k| {
let run_val = run_val.clone();
let k = Arc::new(k);
run_fn(Arc::new(move |f| {
let k = k.clone();
run_val(Arc::new(move |a| k(f(a))))
}))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pure_and_run() {
let cont = ContinuatioT::<i32, i32>::pure(42);
assert_eq!(cont.run(|x| x), 42);
assert_eq!(cont.run(|x| x * 2), 84);
}
#[test]
fn test_map() {
let cont = ContinuatioT::<i32, i32>::pure(21);
let doubled = cont.map(|x| x * 2);
assert_eq!(doubled.run(|x| x), 42);
}
#[test]
fn test_bind_chain() {
let result = ContinuatioT::<i32, i32>::pure(5)
.bind(|x| ContinuatioT::pure(x + 3))
.bind(|x| ContinuatioT::pure(x * 2))
.run(|x| x);
assert_eq!(result, 16); }
#[test]
fn test_call_cc_early_exit() {
let result = ContinuatioT::<i32, i32>::call_cc(|exit| {
if 10 > 5 {
exit(100)
} else {
ContinuatioT::pure(0)
}
})
.run(|x| x);
assert_eq!(result, 100);
}
#[test]
fn test_call_cc_no_exit() {
let result = ContinuatioT::<i32, i32>::call_cc(
|_exit: Arc<dyn Fn(i32) -> ContinuatioT<i32, i32> + Send + Sync>| {
ContinuatioT::pure(42)
},
)
.run(|x| x);
assert_eq!(result, 42);
}
#[test]
fn test_left_identity() {
let a = 5;
let f = |x: i32| ContinuatioT::pure(x * 2);
let left = ContinuatioT::<i32, i32>::pure(a).bind(f);
let right = f(a);
assert_eq!(left.run(|x| x), right.run(|x| x));
}
#[test]
fn test_right_identity() {
let m = ContinuatioT::<i32, i32>::pure(42);
let bound = m.clone().bind(ContinuatioT::pure);
assert_eq!(m.run(|x| x), bound.run(|x| x));
}
#[test]
fn test_associativity() {
let m = ContinuatioT::<i32, i32>::pure(5);
let left = m
.clone()
.bind(|x| ContinuatioT::pure(x + 1))
.bind(|x| ContinuatioT::pure(x * 2));
let right = m.bind(|x| {
let inner = ContinuatioT::pure(x + 1);
inner.bind(|y| ContinuatioT::pure(y * 2))
});
assert_eq!(left.run(|x| x), right.run(|x| x));
}
}