use alloc::boxed::Box;
use core::any::TypeId;
use core::marker::PhantomData;
use crate::nexus::effect::{Eff, EffectMarker};
use crate::nexus::row::{Row, STATE_BIT};
#[derive(Copy, Clone, Debug)]
pub struct StateEffect<S> {
_marker: PhantomData<S>,
}
impl<S> EffectMarker for StateEffect<S> {
const BIT: u128 = STATE_BIT;
const NAME: &'static str = "State";
}
pub type StateRow = Row<STATE_BIT>;
pub enum StateOp<S> {
Get,
Put(S),
}
impl<S: Clone> Clone for StateOp<S> {
fn clone(&self) -> Self {
match self {
StateOp::Get => StateOp::Get,
StateOp::Put(s) => StateOp::Put(s.clone()),
}
}
}
pub fn state_get<S: Clone + 'static>() -> Eff<StateRow, S> {
Eff::lazy(|| {
crate::cold_panic!("state_get requires State handler")
})
}
pub fn state_put<S: 'static>(_value: S) -> Eff<StateRow, ()> {
Eff::lazy(move || crate::cold_panic!("state_put requires State handler"))
}
pub fn state_modify<S: 'static, F: FnOnce(S) -> S + 'static>(_f: F) -> Eff<StateRow, ()> {
Eff::lazy(|| crate::cold_panic!("state_modify requires State handler"))
}
pub fn state_gets<S: 'static, A: 'static, F: Fn(&S) -> A + 'static>(_f: F) -> Eff<StateRow, A> {
Eff::lazy(|| crate::cold_panic!("state_gets requires State handler"))
}
#[must_use = "computations do nothing unless run"]
#[repr(u8)]
pub enum StatefulComputation<S, A> {
Pure(A),
Get,
Put(S),
Modify(Box<dyn FnOnce(S) -> S>),
Boxed(Box<dyn FnOnce(S) -> (A, S)>),
}
impl<S: Clone + 'static, A: 'static> StatefulComputation<S, A> {
#[inline(always)]
pub fn new<F: FnOnce(S) -> (A, S) + 'static>(f: F) -> Self {
StatefulComputation::Boxed(Box::new(f))
}
#[inline(always)]
pub fn pure(value: A) -> Self {
StatefulComputation::Pure(value)
}
#[inline(always)]
pub fn map<B: 'static, F: FnOnce(A) -> B + 'static>(self, f: F) -> StatefulComputation<S, B> {
match self {
StatefulComputation::Pure(a) => StatefulComputation::Pure(f(a)),
_ => StatefulComputation::Boxed(Box::new(move |s| {
let (a, s2) = self.run_internal(s);
(f(a), s2)
})),
}
}
#[inline(always)]
pub fn and_then<B: 'static, F: FnOnce(A) -> StatefulComputation<S, B> + 'static>(
self,
f: F,
) -> StatefulComputation<S, B> {
StatefulComputation::Boxed(Box::new(move |s| {
let (a, s2) = self.run_internal(s);
f(a).run_internal(s2)
}))
}
#[inline]
fn run_internal(self, state: S) -> (A, S) {
match self {
StatefulComputation::Pure(a) => (a, state),
StatefulComputation::Boxed(f) => f(state),
StatefulComputation::Get => {
assert!(
TypeId::of::<A>() == TypeId::of::<S>(),
"StatefulComputation::Get type mismatch: A must be S"
);
let mut opt: Option<S> = Some(state.clone());
let any_opt = &mut opt as &mut dyn core::any::Any;
let downcast: &mut Option<A> = any_opt.downcast_mut::<Option<A>>().unwrap();
let a: A = downcast.take().unwrap();
(a, state)
}
StatefulComputation::Put(new_state) => {
assert!(
TypeId::of::<A>() == TypeId::of::<()>(),
"StatefulComputation::Put type mismatch: A must be ()"
);
let mut opt: Option<()> = Some(());
let any_opt = &mut opt as &mut dyn core::any::Any;
let downcast: &mut Option<A> = any_opt.downcast_mut::<Option<A>>().unwrap();
let unit: A = downcast.take().unwrap();
(unit, new_state)
}
StatefulComputation::Modify(f) => {
assert!(
TypeId::of::<A>() == TypeId::of::<()>(),
"StatefulComputation::Modify type mismatch: A must be ()"
);
let mut opt: Option<()> = Some(());
let any_opt = &mut opt as &mut dyn core::any::Any;
let downcast: &mut Option<A> = any_opt.downcast_mut::<Option<A>>().unwrap();
let unit: A = downcast.take().unwrap();
(unit, f(state))
}
}
}
}
impl<S: Clone + 'static> StatefulComputation<S, S> {
#[inline(always)]
pub fn get() -> Self {
StatefulComputation::Get
}
#[inline(always)]
pub fn run_get(self, initial: S) -> (S, S) {
match self {
StatefulComputation::Pure(a) => (a, initial),
StatefulComputation::Get => (initial.clone(), initial),
StatefulComputation::Boxed(f) => f(initial),
StatefulComputation::Put(_) => {
panic!("StatefulComputation::Put encountered in run_get where A must be S")
}
StatefulComputation::Modify(_) => {
panic!("StatefulComputation::Modify encountered in run_get where A must be S")
}
}
}
}
impl<S: 'static> StatefulComputation<S, ()> {
#[inline(always)]
pub fn put(value: S) -> Self {
StatefulComputation::Put(value)
}
#[inline(always)]
pub fn modify<F: FnOnce(S) -> S + 'static>(f: F) -> Self {
StatefulComputation::Modify(Box::new(f))
}
#[inline(always)]
pub fn run_unit(self, initial: S) -> ((), S) {
match self {
StatefulComputation::Pure(()) => ((), initial),
StatefulComputation::Put(new_state) => ((), new_state),
StatefulComputation::Modify(f) => ((), f(initial)),
StatefulComputation::Boxed(f) => f(initial),
StatefulComputation::Get => {
panic!("StatefulComputation::Get encountered in run_unit where A must be ()")
}
}
}
}
impl<S: Clone + 'static, A: 'static> StatefulComputation<S, A> {
#[inline]
pub fn run(self, initial: S) -> (A, S) {
match self {
StatefulComputation::Pure(a) => (a, initial),
StatefulComputation::Boxed(f) => f(initial),
StatefulComputation::Get => {
assert!(
TypeId::of::<A>() == TypeId::of::<S>(),
"StatefulComputation::Get type mismatch: A must be S"
);
let result = initial.clone();
let mut opt: Option<S> = Some(result);
let any_opt = &mut opt as &mut dyn core::any::Any;
let downcast: &mut Option<A> = any_opt.downcast_mut::<Option<A>>().unwrap();
let a: A = downcast.take().unwrap();
(a, initial)
}
StatefulComputation::Put(new_state) => {
assert!(
TypeId::of::<A>() == TypeId::of::<()>(),
"StatefulComputation::Put type mismatch: A must be ()"
);
let mut opt: Option<()> = Some(());
let any_opt = &mut opt as &mut dyn core::any::Any;
let downcast: &mut Option<A> = any_opt.downcast_mut::<Option<A>>().unwrap();
let unit: A = downcast.take().unwrap();
(unit, new_state)
}
StatefulComputation::Modify(f) => {
assert!(
TypeId::of::<A>() == TypeId::of::<()>(),
"StatefulComputation::Modify type mismatch: A must be ()"
);
let mut opt: Option<()> = Some(());
let any_opt = &mut opt as &mut dyn core::any::Any;
let downcast: &mut Option<A> = any_opt.downcast_mut::<Option<A>>().unwrap();
let unit: A = downcast.take().unwrap();
(unit, f(initial))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stateful_pure() {
let comp = StatefulComputation::<i32, i32>::pure(42);
let (result, state) = comp.run(0);
assert_eq!(result, 42);
assert_eq!(state, 0);
}
#[test]
fn test_stateful_get() {
let comp = StatefulComputation::<i32, i32>::get();
let (result, state) = comp.run(42);
assert_eq!(result, 42);
assert_eq!(state, 42);
}
#[test]
fn test_stateful_put() {
let comp = StatefulComputation::<i32, ()>::put(42);
let ((), state) = comp.run(0);
assert_eq!(state, 42);
}
#[test]
fn test_stateful_modify() {
let comp = StatefulComputation::<i32, ()>::modify(|x| x + 10);
let ((), state) = comp.run(32);
assert_eq!(state, 42);
}
#[test]
fn test_stateful_map() {
let comp = StatefulComputation::<i32, i32>::get().map(|x| x * 2);
let (result, state) = comp.run(21);
assert_eq!(result, 42);
assert_eq!(state, 21);
}
#[test]
fn test_stateful_and_then() {
let comp = StatefulComputation::<i32, i32>::get()
.and_then(|x| StatefulComputation::<i32, ()>::put(x + 10).map(move |()| x));
let (result, state) = comp.run(32);
assert_eq!(result, 32);
assert_eq!(state, 42);
}
#[test]
fn test_stateful_chain() {
let comp = StatefulComputation::<i32, ()>::modify(|x| x + 10)
.and_then(|()| StatefulComputation::<i32, i32>::get())
.map(|x| x * 2);
let (result, state) = comp.run(11);
assert_eq!(result, 42);
assert_eq!(state, 21);
}
}