algar 0.4.1

Algebraic structures, higher-kinded types and other category theory bad ideas
Documentation
use algar::{m, Monad, Monoid, ResultT, StateT, Writer};

use super::{
    turtle::Turtle,
    turtle_api::{self, TurtleError},
};

pub fn lift_op<'a, A: 'a>(
    op_fun: impl Fn(Turtle, A) -> Turtle + 'static,
) -> impl FnOnce(A) -> StateT<'a, Turtle, ResultT<Writer<Result<((), Turtle), TurtleError>, String>>>
{
    move |p: A| {
        StateT::new(move |s| {
            ResultT::lift(Writer::new(
                ((), op_fun(s, p)),
                <String as Monoid>::mempty(),
            ))
        })
    }
}

pub fn lift_validation<'a, A: 'a>(
    validate_fun: impl Fn(String) -> Result<A, TurtleError>,
) -> impl Fn(&str) -> StateT<'a, Turtle, ResultT<Writer<Result<(A, Turtle), TurtleError>, String>>>
{
    move |p: &str| {
        StateT::<'a, Turtle, ResultT<Writer<Result<(A, Turtle), TurtleError>, String>>>::lift(
            ResultT::new(Writer::new(
                validate_fun(p.to_string()),
                <String as Monoid>::mempty(),
            )),
        )
    }
}

pub fn log_info(
    l: String,
) -> StateT<'static, Turtle, ResultT<Writer<Result<((), Turtle), TurtleError>, String>>> {
    StateT::<Turtle, ResultT<Writer<Result<(), TurtleError>, String>>>::lift(ResultT::lift(
        Writer::<Turtle, _>::tell(String::from(l)),
    ))
}

pub fn move_turtle(
    d: &str,
) -> StateT<Turtle, ResultT<Writer<Result<((), Turtle), TurtleError>, String>>> {
    let validate_distance = lift_validation(turtle_api::validate_distance);
    let do_move = lift_op(Turtle::r#move);

    m! {
        log_info(format!("moving {}\n", d));
        distance <- validate_distance(d);
        do_move(distance)
    }
}

pub fn turn_turtle(
    a: &str,
) -> StateT<Turtle, ResultT<Writer<Result<((), Turtle), TurtleError>, String>>> {
    let validate_angle = lift_validation(turtle_api::validate_angle);
    let do_turn = lift_op(Turtle::turn);

    m! {
        log_info(format!("turning {}\n", a));
        angle <- validate_angle(a);
        do_turn(angle)
    }
}

pub fn set_turtle_color(
    c: &str,
) -> StateT<Turtle, ResultT<Writer<Result<((), Turtle), TurtleError>, String>>> {
    let validate_pen_color = lift_validation(turtle_api::validate_pen_color);
    let do_set_pen_color = lift_op(Turtle::set_pen_color);

    m! {
        log_info(format!("coloring pen to  {}\n", c));
        color <- validate_pen_color(c);
        do_set_pen_color(color)
    }
}

pub fn run(
    computation: StateT<Turtle, ResultT<Writer<Result<((), Turtle), TurtleError>, String>>>,
    t: Turtle,
) -> (Result<((), Turtle), TurtleError>, String) {
    Writer::execute(ResultT::execute(StateT::execute(computation, t)))
}