use core::marker::PhantomData;
pub trait ReaderOp<E> {
type Output;
fn run_reader(self, env: &E) -> Self::Output;
}
pub struct PureReader<A>(pub A);
impl<E, A> ReaderOp<E> for PureReader<A> {
type Output = A;
#[inline(always)]
fn run_reader(self, _env: &E) -> A {
self.0
}
}
pub struct AskReader<E>(PhantomData<E>);
impl<E: Clone> ReaderOp<E> for AskReader<E> {
type Output = E;
#[inline(always)]
fn run_reader(self, env: &E) -> E {
env.clone()
}
}
pub struct AsksReader<E, A, F: FnOnce(&E) -> A>(pub F, PhantomData<(E, A)>);
impl<E, A, F: FnOnce(&E) -> A> ReaderOp<E> for AsksReader<E, A, F> {
type Output = A;
#[inline(always)]
fn run_reader(self, env: &E) -> A {
(self.0)(env)
}
}
pub struct MapReader<Op, F>(pub Op, pub F);
impl<E, Op: ReaderOp<E>, B, F: FnOnce(Op::Output) -> B> ReaderOp<E> for MapReader<Op, F> {
type Output = B;
#[inline(always)]
fn run_reader(self, env: &E) -> B {
(self.1)(self.0.run_reader(env))
}
}
pub struct AndThenReader<Op1, F>(pub Op1, pub F);
impl<E, Op1: ReaderOp<E>, Op2: ReaderOp<E>, F: FnOnce(Op1::Output) -> Op2> ReaderOp<E>
for AndThenReader<Op1, F>
{
type Output = Op2::Output;
#[inline(always)]
fn run_reader(self, env: &E) -> Op2::Output {
let a = self.0.run_reader(env);
(self.1)(a).run_reader(env)
}
}
pub struct LocalReader<Op, F>(pub Op, pub F);
impl<E, Op: ReaderOp<E>, F: FnOnce(&E) -> E> ReaderOp<E> for LocalReader<Op, F> {
type Output = Op::Output;
#[inline(always)]
fn run_reader(self, env: &E) -> Op::Output {
let new_env = (self.1)(env);
self.0.run_reader(&new_env)
}
}
pub trait ReaderOpExt<E>: ReaderOp<E> + Sized {
#[inline(always)]
fn map_reader<B, F: FnOnce(Self::Output) -> B>(self, f: F) -> MapReader<Self, F> {
MapReader(self, f)
}
#[inline(always)]
fn and_then_reader<Op2: ReaderOp<E>, F: FnOnce(Self::Output) -> Op2>(
self,
f: F,
) -> AndThenReader<Self, F> {
AndThenReader(self, f)
}
#[inline(always)]
fn local_reader<F: FnOnce(&E) -> E>(self, f: F) -> LocalReader<Self, F> {
LocalReader(self, f)
}
}
impl<E, Op: ReaderOp<E>> ReaderOpExt<E> for Op {}
#[inline(always)]
pub fn pure_reader<A>(a: A) -> PureReader<A> {
PureReader(a)
}
#[inline(always)]
pub fn ask_reader<E>() -> AskReader<E> {
AskReader(PhantomData)
}
#[inline(always)]
pub fn asks_reader<E, A, F: FnOnce(&E) -> A>(f: F) -> AsksReader<E, A, F> {
AsksReader(f, PhantomData)
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone)]
struct TestEnv {
value: i32,
multiplier: i32,
}
#[test]
fn test_reader_op_pure() {
let op = pure_reader(42);
let env = TestEnv {
value: 0,
multiplier: 1,
};
assert_eq!(op.run_reader(&env), 42);
}
#[test]
fn test_reader_op_ask() {
let op = ask_reader::<TestEnv>();
let env = TestEnv {
value: 42,
multiplier: 2,
};
let result = op.run_reader(&env);
assert_eq!(result.value, 42);
}
#[test]
fn test_reader_op_asks() {
let op = asks_reader(|e: &TestEnv| e.value);
let env = TestEnv {
value: 42,
multiplier: 2,
};
assert_eq!(op.run_reader(&env), 42);
}
#[test]
fn test_reader_op_map() {
let op = asks_reader(|e: &TestEnv| e.value).map_reader(|x| x * 2);
let env = TestEnv {
value: 21,
multiplier: 2,
};
assert_eq!(op.run_reader(&env), 42);
}
#[test]
fn test_reader_op_and_then() {
let op = asks_reader(|e: &TestEnv| e.value)
.and_then_reader(|v| asks_reader(move |e: &TestEnv| v * e.multiplier));
let env = TestEnv {
value: 21,
multiplier: 2,
};
assert_eq!(op.run_reader(&env), 42);
}
#[test]
fn test_reader_op_local() {
let op = asks_reader(|e: &TestEnv| e.value).local_reader(|e| TestEnv {
value: e.value + 10,
multiplier: e.multiplier,
});
let env = TestEnv {
value: 32,
multiplier: 1,
};
assert_eq!(op.run_reader(&env), 42);
}
#[test]
fn test_reader_op_chain() {
let op = asks_reader(|e: &TestEnv| e.value)
.and_then_reader(|v| asks_reader(move |e: &TestEnv| v + e.multiplier))
.and_then_reader(|v| asks_reader(move |e: &TestEnv| v * e.multiplier))
.map_reader(|x| x + 1);
let env = TestEnv {
value: 10,
multiplier: 2,
};
assert_eq!(op.run_reader(&env), 25);
}
#[test]
fn test_reader_op_long_chain() {
let op = asks_reader(|e: &TestEnv| e.value)
.and_then_reader(|acc| asks_reader(move |e: &TestEnv| acc + e.value))
.and_then_reader(|acc| asks_reader(move |e: &TestEnv| acc + e.value))
.and_then_reader(|acc| asks_reader(move |e: &TestEnv| acc + e.value))
.and_then_reader(|acc| asks_reader(move |e: &TestEnv| acc + e.value))
.and_then_reader(|acc| asks_reader(move |e: &TestEnv| acc + e.value))
.and_then_reader(|acc| asks_reader(move |e: &TestEnv| acc + e.value))
.and_then_reader(|acc| asks_reader(move |e: &TestEnv| acc + e.value))
.and_then_reader(|acc| asks_reader(move |e: &TestEnv| acc + e.value))
.and_then_reader(|acc| asks_reader(move |e: &TestEnv| acc + e.value));
let env = TestEnv {
value: 1,
multiplier: 0,
};
assert_eq!(op.run_reader(&env), 10);
}
}