use alloc::boxed::Box;
use core::marker::PhantomData;
use super::row::{EffectRow, ErrorRow, Pure, ReaderRow, StateRow};
#[repr(transparent)]
pub struct PureRepr<A> {
pub value: A,
}
impl<A> PureRepr<A> {
#[inline(always)]
pub const fn new(value: A) -> Self {
PureRepr { value }
}
#[inline(always)]
pub fn run(self) -> A {
self.value
}
#[inline(always)]
pub fn map<B, F: FnOnce(A) -> B>(self, f: F) -> PureRepr<B> {
PureRepr::new(f(self.value))
}
}
pub struct StateRepr<S, A> {
run_fn: Box<dyn FnOnce(S) -> (A, S)>,
}
impl<S: 'static, A: 'static> StateRepr<S, A> {
#[inline]
pub fn new<F: FnOnce(S) -> (A, S) + 'static>(f: F) -> Self {
StateRepr {
run_fn: Box::new(f),
}
}
#[inline]
pub fn run(self, initial: S) -> (A, S) {
(self.run_fn)(initial)
}
#[inline]
pub fn get() -> StateRepr<S, S>
where
S: Clone,
{
StateRepr::new(|s: S| (s.clone(), s))
}
#[inline]
pub fn put(new_state: S) -> StateRepr<S, ()> {
StateRepr::new(move |_| ((), new_state))
}
#[inline]
pub fn modify<F: FnOnce(S) -> S + 'static>(f: F) -> StateRepr<S, ()> {
StateRepr::new(move |s| ((), f(s)))
}
pub fn map<B: 'static, F: FnOnce(A) -> B + 'static>(self, f: F) -> StateRepr<S, B> {
StateRepr::new(move |s| {
let (a, s2) = (self.run_fn)(s);
(f(a), s2)
})
}
pub fn and_then<B: 'static, F: FnOnce(A) -> StateRepr<S, B> + 'static>(
self,
f: F,
) -> StateRepr<S, B> {
StateRepr::new(move |s| {
let (a, s2) = (self.run_fn)(s);
f(a).run(s2)
})
}
}
pub struct ReaderRepr<E, A> {
run_fn: Box<dyn FnOnce(&E) -> A>,
}
impl<E: 'static, A: 'static> ReaderRepr<E, A> {
#[inline]
pub fn new<F: FnOnce(&E) -> A + 'static>(f: F) -> Self {
ReaderRepr {
run_fn: Box::new(f),
}
}
#[inline]
pub fn run(self, env: &E) -> A {
(self.run_fn)(env)
}
#[inline]
pub fn ask() -> ReaderRepr<E, E>
where
E: Clone,
{
ReaderRepr::new(|e: &E| e.clone())
}
#[inline]
pub fn asks<F: FnOnce(&E) -> A + 'static>(f: F) -> Self {
ReaderRepr::new(f)
}
pub fn map<B: 'static, F: FnOnce(A) -> B + 'static>(self, f: F) -> ReaderRepr<E, B> {
ReaderRepr::new(move |e| f((self.run_fn)(e)))
}
pub fn and_then<B: 'static, F: FnOnce(A) -> ReaderRepr<E, B> + 'static>(
self,
f: F,
) -> ReaderRepr<E, B> {
ReaderRepr::new(move |e| {
let a = (self.run_fn)(e);
f(a).run(e)
})
}
}
pub struct ErrorRepr<E, A> {
pub result: Result<A, E>,
}
impl<E, A> ErrorRepr<E, A> {
#[inline(always)]
pub const fn ok(value: A) -> Self {
ErrorRepr { result: Ok(value) }
}
#[inline(always)]
pub const fn err(error: E) -> Self {
ErrorRepr { result: Err(error) }
}
#[inline(always)]
pub fn run(self) -> Result<A, E> {
self.result
}
pub fn map<B, F: FnOnce(A) -> B>(self, f: F) -> ErrorRepr<E, B> {
ErrorRepr {
result: self.result.map(f),
}
}
pub fn and_then<B, F: FnOnce(A) -> ErrorRepr<E, B>>(self, f: F) -> ErrorRepr<E, B> {
match self.result {
Ok(a) => f(a),
Err(e) => ErrorRepr::err(e),
}
}
}
pub struct ContRepr<R: EffectRow, A> {
inner: Box<dyn FnOnce() -> A>,
_marker: PhantomData<R>,
}
impl<R: EffectRow + 'static, A: 'static> ContRepr<R, A> {
pub fn pure(value: A) -> Self {
ContRepr {
inner: Box::new(move || value),
_marker: PhantomData,
}
}
pub fn run(self) -> A {
(self.inner)()
}
pub fn map<B: 'static, F: FnOnce(A) -> B + 'static>(self, f: F) -> ContRepr<R, B> {
ContRepr {
inner: Box::new(move || f((self.inner)())),
_marker: PhantomData,
}
}
}
pub trait EffRepr<R: EffectRow, A>: Sized {}
impl<A> EffRepr<Pure, A> for PureRepr<A> {}
impl<S: 'static, A: 'static> EffRepr<StateRow, A> for StateRepr<S, A> {}
impl<E: 'static, A: 'static> EffRepr<ReaderRow, A> for ReaderRepr<E, A> {}
impl<Err, A> EffRepr<ErrorRow, A> for ErrorRepr<Err, A> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pure_repr() {
let repr = PureRepr::new(42);
assert_eq!(repr.run(), 42);
}
#[test]
fn test_pure_map() {
let repr = PureRepr::new(21);
let mapped = repr.map(|x| x * 2);
assert_eq!(mapped.run(), 42);
}
#[test]
fn test_state_repr() {
let repr = StateRepr::<i32, i32>::get();
let (value, state) = repr.run(42);
assert_eq!(value, 42);
assert_eq!(state, 42);
}
#[test]
fn test_state_put() {
let repr = StateRepr::<i32, ()>::put(100);
let ((), state) = repr.run(42);
assert_eq!(state, 100);
}
#[test]
fn test_state_modify() {
let repr = StateRepr::<i32, ()>::modify(|x| x + 10);
let ((), state) = repr.run(32);
assert_eq!(state, 42);
}
#[test]
fn test_reader_repr() {
let repr = ReaderRepr::<i32, i32>::ask();
let value = repr.run(&42);
assert_eq!(value, 42);
}
#[test]
fn test_reader_asks() {
let repr = ReaderRepr::<(i32, i32), i32>::asks(|(a, b)| a + b);
let value = repr.run(&(20, 22));
assert_eq!(value, 42);
}
#[test]
fn test_error_ok() {
let repr: ErrorRepr<&str, i32> = ErrorRepr::ok(42);
assert_eq!(repr.run(), Ok(42));
}
#[test]
fn test_error_err() {
let repr: ErrorRepr<&str, i32> = ErrorRepr::err("oops");
assert_eq!(repr.run(), Err("oops"));
}
#[test]
fn test_error_map() {
let repr: ErrorRepr<&str, i32> = ErrorRepr::ok(21);
let mapped = repr.map(|x| x * 2);
assert_eq!(mapped.run(), Ok(42));
}
#[test]
fn test_cont_pure() {
let repr: ContRepr<Pure, i32> = ContRepr::pure(42);
assert_eq!(repr.run(), 42);
}
#[test]
fn test_cont_map() {
let repr: ContRepr<Pure, i32> = ContRepr::pure(21);
let mapped = repr.map(|x| x * 2);
assert_eq!(mapped.run(), 42);
}
}