stackless
This commit is contained in:
parent
504c0abc4c
commit
6e0dfd4b5e
@ -7,7 +7,7 @@ pub mod test_suite;
|
||||
pub mod tests;
|
||||
|
||||
pub trait WeakFunctor {
|
||||
type F<'a, A>;
|
||||
type F<'a, A: 'a> : 'a;
|
||||
}
|
||||
|
||||
/// Rust-specific implementation of [Functor], respecting `move` semantics.
|
||||
|
@ -1,6 +1,7 @@
|
||||
pub mod compositionclass;
|
||||
pub mod futureclass;
|
||||
pub mod lazyclass;
|
||||
pub mod optionclass;
|
||||
pub mod resultclass;
|
||||
pub mod soloclass;
|
||||
pub mod composition;
|
||||
pub mod future;
|
||||
pub mod lazy;
|
||||
pub mod option;
|
||||
pub mod result;
|
||||
pub mod solo;
|
||||
pub mod stackless;
|
||||
|
@ -3,7 +3,7 @@ use crate::func::*;
|
||||
struct CompositionClass<U, V>(U, V);
|
||||
|
||||
impl<U: WeakFunctor, V: WeakFunctor> WeakFunctor for CompositionClass<U, V> {
|
||||
type F<'a, A> = U::F<'a, V::F<'a, A>>;
|
||||
type F<'a, A: 'a> = U::F<'a, V::F<'a, A>>;
|
||||
}
|
||||
|
||||
impl<U: Functor, V: Functor> Functor for CompositionClass<U, V> {
|
@ -7,7 +7,7 @@ use crate::func::*;
|
||||
pub struct FutureClass;
|
||||
|
||||
impl WeakFunctor for FutureClass {
|
||||
type F<'a, A> = Pin<Box<dyn 'a + Future<Output = A>>>;
|
||||
type F<'a, A: 'a> = Pin<Box<dyn 'a + Future<Output = A>>>;
|
||||
}
|
||||
|
||||
impl Functor for FutureClass {
|
@ -3,7 +3,7 @@ use crate::func::*;
|
||||
struct LazyClass;
|
||||
|
||||
impl WeakFunctor for LazyClass {
|
||||
type F<'a, A> = Box<dyn 'a + FnOnce() -> A>;
|
||||
type F<'a, A: 'a> = Box<dyn 'a + FnOnce() -> A>;
|
||||
}
|
||||
|
||||
impl Functor for LazyClass {
|
@ -3,7 +3,7 @@ use crate::func::*;
|
||||
pub struct OptionClass;
|
||||
|
||||
impl WeakFunctor for OptionClass {
|
||||
type F<'a, A> = Option<A>;
|
||||
type F<'a, A: 'a> = Option<A>;
|
||||
}
|
||||
|
||||
impl Functor for OptionClass {
|
||||
@ -66,12 +66,12 @@ impl Monad for OptionClass {
|
||||
|
||||
#[cfg(test)]
|
||||
mod option_tests {
|
||||
use super::{test_suite, tests};
|
||||
use super::{test_suite, tests, Functor};
|
||||
|
||||
use super::{Functor, OptionClass as T};
|
||||
use super::OptionClass as T;
|
||||
|
||||
impl tests::Eqr for T {
|
||||
fn eqr<'a, A: PartialEq + std::fmt::Debug>(
|
||||
fn eqr<'a, A: PartialEq + std::fmt::Debug + 'a>(
|
||||
name: &'a str,
|
||||
left: Self::F<'a, A>,
|
||||
right: Self::F<'a, A>,
|
@ -1,9 +1,9 @@
|
||||
use crate::func::*;
|
||||
|
||||
pub struct ResultClass<E>(E);
|
||||
pub struct ResultClass<E: 'static>(E);
|
||||
|
||||
impl<E> WeakFunctor for ResultClass<E> {
|
||||
type F<'a, A> = Result<A, E>;
|
||||
type F<'a, A: 'a> = Result<A, E>;
|
||||
}
|
||||
|
||||
impl<E> Functor for ResultClass<E> {
|
@ -3,7 +3,7 @@ use crate::func::*;
|
||||
pub struct SoloClass;
|
||||
|
||||
impl WeakFunctor for SoloClass {
|
||||
type F<'a, A> = A;
|
||||
type F<'a, A: 'a> = A;
|
||||
}
|
||||
|
||||
impl Functor for SoloClass {
|
278
src/func/classes/stackless.rs
Normal file
278
src/func/classes/stackless.rs
Normal file
@ -0,0 +1,278 @@
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
|
||||
use crate::func::*;
|
||||
|
||||
enum EvalTree<'a> {
|
||||
Atom(Box<dyn 'a + FnOnce() -> Option<EvalTree<'a>>>),
|
||||
Composite(Box<EvalTree<'a>>, Box<EvalTree<'a>>),
|
||||
}
|
||||
|
||||
impl<'a> EvalTree<'a> {
|
||||
fn next(self) -> Option<EvalTree<'a>> {
|
||||
match self {
|
||||
EvalTree::Atom(f) => f(),
|
||||
EvalTree::Composite(left, right) => match *left {
|
||||
EvalTree::Atom(f) => match f() {
|
||||
Some(newleft) => Some(EvalTree::Composite(Box::new(newleft), right)),
|
||||
None => Some(*right),
|
||||
},
|
||||
EvalTree::Composite(leftleft, leftright) => Some(EvalTree::Composite(
|
||||
leftleft,
|
||||
Box::new(EvalTree::Composite(leftright, right)),
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Stackless<'a, A: 'a>(
|
||||
Box<dyn 'a + FnOnce(Box<dyn 'a + FnOnce(A)>) -> Option<EvalTree<'a>>>,
|
||||
);
|
||||
|
||||
fn set_cell<A>(cell: Rc<Cell<Option<A>>>, a: A) -> () {
|
||||
if cell.replace(Some(a)).is_some() {
|
||||
panic!("MITM overwritten")
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cell<A>(cell: Rc<Cell<Option<A>>>) -> A {
|
||||
match cell.replace(None) {
|
||||
Some(val) => val,
|
||||
None => panic!("MITM not set"),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: 'a> Stackless<'a, A> {
|
||||
fn call(self, f: impl 'a + FnOnce(A)) -> Option<EvalTree<'a>> {
|
||||
self.0(Box::new(f))
|
||||
}
|
||||
|
||||
fn bind<B: 'a>(self, f: impl 'a + FnOnce(A) -> Stackless<'a, B>) -> Stackless<'a, B> {
|
||||
Stackless(Box::new(|takesb| {
|
||||
let lcell = Rc::new(Cell::new(None));
|
||||
let rcell = lcell.clone();
|
||||
Some(EvalTree::Composite(
|
||||
Box::new(EvalTree::Atom(Box::new(move || {
|
||||
self.call(move |a| set_cell(lcell, a))
|
||||
}))),
|
||||
Box::new(EvalTree::Atom(Box::new(move || {
|
||||
let stackless = f(get_cell(rcell));
|
||||
Some(EvalTree::Atom(Box::new(|| stackless.0(takesb))))
|
||||
}))),
|
||||
))
|
||||
}))
|
||||
}
|
||||
|
||||
fn map<B: 'a>(self, f: impl 'a + FnOnce(A) -> B) -> Stackless<'a, B> {
|
||||
Stackless(Box::new(|takesb| {
|
||||
let lcell = Rc::new(Cell::new(None));
|
||||
let rcell = lcell.clone();
|
||||
Some(EvalTree::Composite(
|
||||
Box::new(EvalTree::Atom(Box::new(move || {
|
||||
self.call(move |a| set_cell(lcell, a))
|
||||
}))),
|
||||
Box::new(EvalTree::Atom(Box::new(move || {
|
||||
let b = f(get_cell(rcell));
|
||||
Some(EvalTree::Atom(Box::new(|| {
|
||||
takesb(b);
|
||||
None
|
||||
})))
|
||||
}))),
|
||||
))
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn evaluate(self) -> A {
|
||||
let ocell = Rc::new(Cell::new(None));
|
||||
let icell = ocell.clone();
|
||||
let mut eval = self.call(|a| set_cell(icell, a));
|
||||
while let Some(tree) = eval {
|
||||
eval = tree.next()
|
||||
}
|
||||
get_cell(ocell)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A: 'a> From<A> for Stackless<'a, A> {
|
||||
fn from(value: A) -> Self {
|
||||
Stackless(Box::new(|takesa| {
|
||||
Some(EvalTree::Atom(Box::new(|| {
|
||||
takesa(value);
|
||||
None
|
||||
})))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
struct StacklessClass;
|
||||
|
||||
impl WeakFunctor for StacklessClass {
|
||||
type F<'a, A: 'a> = Stackless<'a, A>;
|
||||
}
|
||||
|
||||
impl Functor for StacklessClass {
|
||||
fn fmap<'a, A: 'a, B: 'a>(f: impl 'a + FnOnce(A) -> B, fa: Self::F<'a, A>) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
fa.map(f)
|
||||
}
|
||||
|
||||
fn replace<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, b: B) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Stackless(Box::new(|takesb| {
|
||||
Some(EvalTree::Composite(
|
||||
Box::new(EvalTree::Atom(Box::new(move || fa.call(drop)))),
|
||||
Box::new(EvalTree::Atom(Box::new(move || {
|
||||
takesb(b);
|
||||
None
|
||||
}))),
|
||||
))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicativeSeq for StacklessClass {
|
||||
fn seq<'a, A: 'a, B: 'a>(
|
||||
ff: Self::F<'a, impl 'a + FnOnce(A) -> B>,
|
||||
fa: Self::F<'a, A>,
|
||||
) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
ff.bind(|f| fa.map(f))
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicativeLA2 for StacklessClass {
|
||||
fn la2<'a, A: 'a, B: 'a, C: 'a>(
|
||||
f: impl 'a + FnOnce(A, B) -> C,
|
||||
fa: Self::F<'a, A>,
|
||||
fb: Self::F<'a, B>,
|
||||
) -> Self::F<'a, C>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Self::_la2(f, fa, fb)
|
||||
}
|
||||
}
|
||||
|
||||
impl Applicative for StacklessClass {
|
||||
fn pure<'a, A: 'a>(a: A) -> Self::F<'a, A> {
|
||||
Stackless::from(a)
|
||||
}
|
||||
|
||||
fn discard_first<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Stackless(Box::new(|takesb| {
|
||||
Some(EvalTree::Composite(
|
||||
Box::new(EvalTree::Atom(Box::new(|| fa.call(drop)))),
|
||||
Box::new(EvalTree::Atom(Box::new(|| fb.0(takesb)))),
|
||||
))
|
||||
}))
|
||||
}
|
||||
|
||||
fn discard_second<'a, A: 'a, B: 'a>(fa: Self::F<'a, A>, fb: Self::F<'a, B>) -> Self::F<'a, A>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
Stackless(Box::new(|takesa| {
|
||||
Some(EvalTree::Composite(
|
||||
Box::new(EvalTree::Atom(Box::new(|| fa.0(takesa)))),
|
||||
Box::new(EvalTree::Atom(Box::new(|| fb.call(drop)))),
|
||||
))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Monad for StacklessClass {
|
||||
fn bind<'a, A: 'a, B: 'a>(
|
||||
fa: Self::F<'a, A>,
|
||||
f: impl 'a + FnOnce(A) -> Self::F<'a, B>,
|
||||
) -> Self::F<'a, B>
|
||||
where
|
||||
Self: 'a,
|
||||
{
|
||||
fa.bind(f)
|
||||
}
|
||||
fn join<'a, A: 'a>(ffa: Self::F<'a, Self::F<'a, A>>) -> Self::F<'a, A>
|
||||
where
|
||||
Self::F<'a, A>: 'a,
|
||||
Self: 'a,
|
||||
{
|
||||
Stackless(Box::new(|takesa| {
|
||||
let lcell = Rc::new(Cell::new(None));
|
||||
let rcell = lcell.clone();
|
||||
Some(EvalTree::Composite(
|
||||
Box::new(EvalTree::Atom(Box::new(move || {
|
||||
ffa.call(move |a| set_cell(lcell, a))
|
||||
}))),
|
||||
Box::new(EvalTree::Atom(Box::new(move || {
|
||||
let stackless = get_cell(rcell);
|
||||
Some(EvalTree::Atom(Box::new(|| stackless.0(takesa))))
|
||||
}))),
|
||||
))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod stackless_test {
|
||||
use super::{test_suite, tests, Stackless};
|
||||
|
||||
use super::StacklessClass as T;
|
||||
|
||||
impl tests::Eqr for T {
|
||||
fn eqr<'a, A: PartialEq + std::fmt::Debug + 'a>(
|
||||
name: &'a str,
|
||||
left: Self::F<'a, A>,
|
||||
right: Self::F<'a, A>,
|
||||
) -> tests::R {
|
||||
tests::eqr(name, left.evaluate(), right.evaluate())
|
||||
}
|
||||
}
|
||||
|
||||
impl test_suite::FunctorTestSuite for T {
|
||||
fn sample<'a, A: 'a, F: FnMut(&'a dyn Fn(A) -> Self::F<'a, A>)>(mut f: F)
|
||||
where
|
||||
Self::F<'a, A>: 'a,
|
||||
{
|
||||
f(&|a| a.into());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn monad_follows_laws() {
|
||||
test_suite::monad_follows_laws::<T>().unwrap();
|
||||
}
|
||||
|
||||
fn factorial(n: u32) -> Stackless<'static, u32> {
|
||||
if n > 0 {
|
||||
Stackless::from(()).bind(move |_| factorial(n - 1).map(move |acc| acc * n))
|
||||
} else {
|
||||
1.into()
|
||||
}
|
||||
}
|
||||
|
||||
fn dumb(n: u32) -> Stackless<'static, u32> {
|
||||
if n > 0 {
|
||||
Stackless::from(()).bind(move |_| dumb(n - 1).map(move |acc| acc + 1))
|
||||
} else {
|
||||
0.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_factorial() {
|
||||
assert_eq!(factorial(10).evaluate(), 3628800);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dumb() {
|
||||
assert_eq!(dumb(1000).evaluate(), 1000);
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ impl R {
|
||||
}
|
||||
|
||||
pub trait Eqr: WeakFunctor {
|
||||
fn eqr<'a, A: PartialEq + Debug>(
|
||||
fn eqr<'a, A: PartialEq + Debug + 'a>(
|
||||
name: &'a str,
|
||||
left: Self::F<'a, A>,
|
||||
right: Self::F<'a, A>,
|
||||
|
Loading…
Reference in New Issue
Block a user