Current section

Files

Jump to
decepticon src decepticon.gleam
Raw

src/decepticon.gleam

import decepticon/stateful.{type State} as state
import gleam/result
/// The main type of this library- which represents a `State` wrapping a `Result`.
/// See the `decepticon/stateful` module for standalone `State`
pub type StateResult(a, s, err) {
StateResult(run: State(Result(a, err), s))
}
/// Given a `StateResult`, return the `Result` of the last executed action.
pub fn eval(state_result: StateResult(a, s, err), s: s) -> Result(a, err) {
state.eval(state_result.run, s)
}
/// Given a `StateResult`, return the final version of the internal state.
pub fn exec(state_result: StateResult(a, s, err), s: s) -> s {
state.exec(state_result.run, s)
}
/// Lift a `Result` into the `StateResult` type. Useful for when you have a
/// non-stateful action that is simply a `Result` and wish to weave it in
/// as a `use` statement
pub fn try(result_value: Result(a, err)) {
StateResult(run: state.action(result_value))
}
/// Lift a `Result`'s value argument into a `StateResult`
pub fn action(action_value: a) {
StateResult(run: state.action(Ok(action_value)))
}
/// Lift a `Result`'s error argument into a `StateResult`
pub fn error(error: err) {
StateResult(run: state.action(Error(error)))
}
/// Lift a `State` type into a `StateResult` type. Wrapping it's
/// action type in `result.Ok`
pub fn ok_state(state: State(a, s)) -> StateResult(a, s, err) {
StateResult(run: state.map(state, fn(a) { Ok(a) }))
}
/// Lift a `State` type into a `StateResult` type. Wrapping it's
/// action type in `result.Error`
pub fn error_state(state: State(err, s)) -> StateResult(a, s, err) {
StateResult(run: state.map(state, fn(err) { Error(err) }))
}
/// Return the current value of state
pub fn get() -> StateResult(s, s, err) {
ok_state(state.get())
}
/// Set the current value of state
pub fn put(state_value: s) -> StateResult(Nil, s, err) {
ok_state(state.put(state_value))
}
/// Map over the action type of a `StateResult`
pub fn map(
over state_result: StateResult(a, s, err),
with map_fn: fn(a) -> b,
) -> StateResult(b, s, err) {
StateResult(
run: state.map(state_result.run, fn(a_result) {
result.map(a_result, map_fn)
}),
)
}
/// Applicative Functor method for `StateResult`
pub fn apply(
prev: StateResult(fn(a) -> b, s, err),
next: StateResult(a, s, err),
) -> StateResult(b, s, err) {
let combined =
state.do(prev.run, fn(result_fn) {
case result_fn {
Ok(map_fn) -> {
state.map(next.run, fn(arg) { result.map(arg, map_fn) })
}
Error(err) -> state.action(Error(err))
}
})
StateResult(run: combined)
}
/// Map over the error type of a `StateResult`
pub fn map_error(
over state_result: StateResult(a, s, err),
with map_error_fn: fn(err) -> err2,
) -> StateResult(a, s, err2) {
StateResult(
run: state.map(state_result.run, fn(a_result) {
result.map_error(a_result, map_error_fn)
}),
)
}
/// The basic mechanism of chaining `StateResult` actions. This is most similar to `result.try`
pub fn do(
over state_result: StateResult(a, s, err),
with and_then_fn: fn(a) -> StateResult(b, s, err),
) -> StateResult(b, s, err) {
StateResult(
run: state.do(state_result.run, fn(a_result) {
case a_result {
Ok(a) -> and_then_fn(a).run
Error(err) -> state.action(Error(err))
}
}),
)
}