effect
Types
The Effect
type represents computations that might return early or continue with a value.
Each effect specifies:
- The type of the continue value (
msg
) - The type of early return value (
early
)
You can treat msg
as the happy path.
The early
type is for long jumping.
pub opaque type Effect(msg, early)
A function that handles the successful path of an effect.
pub type Next(msg) =
fn(msg) -> Nil
A function that handles the early return path of an effect.
pub type Not(early) =
fn(early) -> Nil
Functions
pub fn continue(value: a) -> Effect(a, b)
Creates an effect that succeeds with the given value.
let effect: Effect(Int, early) = continue(42)
pub fn from(
value: a,
handler: fn(a) -> Effect(b, c),
) -> Effect(b, c)
Creates an effect from a value with a handler to operate on it.
use n: Int <- from(5)
pub fn from_box(
box: a,
unbox_fn: fn(a, fn(b) -> Nil) -> c,
handler: fn(b) -> Effect(d, e),
) -> Effect(d, e)
Creates an effect from a boxed value (like a Promise), using the provided unboxing function (like promise.map) and operates on the unboxed value via the handler.
let promise: Promise(Result(ok, err))
use result: Result(ok, err) <- from_box(promise, promise.map)
use ok: ok <- from_result(result)
pub fn from_option(
value: Option(a),
early: b,
handler: fn(a) -> Effect(c, b),
) -> Effect(c, b)
Creates an effect from an Option, where Some values are passed to the given function and None causes an early return.
pub fn from_result(
value: Result(a, b),
handler: fn(a) -> Effect(c, b),
) -> Effect(c, b)
Creates an effect from a Result, where Ok values are passed to the given function and Error values cause an early return.
let str: String = "123"
let result: Result(Int, Nil) = parse_int(str)
use num: Int <- from_result(result)
pub fn handle(
effect: Effect(a, b),
handler: fn(Result(a, b)) -> Effect(c, d),
) -> Effect(c, d)
Handles both paths of an effect, allowing transformation into a new effect with potentially different types.
type ShowState {
ShowSuccess(valid)
ShowError(err)
}
use res: Result(valid, err) <- handle(validate_input(data))
case res {
Ok(valid) -> continue(ShowSuccess(valid))
Error(err) -> continue(ShowError(err))
}
pub fn map(
effect: Effect(a, b),
handler: fn(a) -> c,
) -> Effect(c, b)
Transforms the successful values of an effect via the handler.
let effect = continue(5)
use num: Int <- map(effect)
num * 2
pub fn map_early(
effect: Effect(a, b),
handler: fn(b) -> c,
) -> Effect(a, c)
Transforms the early return values of an effect via the handler.
type Msg { Msg(String) }
let effect: Effect(msg, String) = throw("some context")
let effect: Effect(msg, Msg) = map_early(effect, Msg)
pub fn perform(
effect: Effect(a, b),
handler: fn(Result(a, b)) -> c,
) -> Nil
Executes an effect, handling both paths with a handler.
use res: Result(msg, early) <- perform(effect)
case res {
Ok(value) -> io.println("Success: " <> value)
Error(err) -> io.println("Error: " <> err)
}
pub fn pure(
effect: Effect(a, Nothing),
handler: fn(a) -> b,
) -> Nil
Executes an effect that is known to be pure (cannot have early returns). The handler only needs to handle the success case.
let effect: Effect(Int, early) = continue(42)
use num: Int <- pure(effect)
num |> int.to_string |> io.println
pub fn then(
effect: Effect(a, b),
handler: fn(a) -> Effect(c, b),
) -> Effect(c, b)
Chains together two effects where the second effect depends on the result of the first.
let effect = {
use a <- then(get_user())
use b <- then(get_posts(a.id))
continue(b)
}
pub fn throw(value: a) -> Effect(b, a)
Creates an effect that returns early with the given value.
let effect: Effect(any, String) = throw("Something went wrong")
pub fn unbox(
box: a,
unbox_fn: fn(a, fn(b) -> Nil) -> c,
) -> Effect(b, d)
Creates an effect from a boxed value. Primarly used to unbox Promises but can work with other boxed types.
let value: Value = ...
let promise: Promise(Value) = promise.resolve(value)
let effect: Effect(Value, early) = unbox(promise, promise.map)
pub fn wrap_option(value: Option(a), early: b) -> Effect(a, b)
Creates an effect that wraps an option type. The Some variant continues and the None variant returns early with the given early value.
let effect: Effect(Int, String) = wrap_option(Some(69), "It should've been Some tho...")
let effect: Effect(Int, String) = wrap_option(None, "This is None")
pub fn wrap_result(value: Result(a, b)) -> Effect(a, b)
Creates an effect that wraps a result type. The Ok variant continues and the Error variant returns early.
let effect: Effect(Int, early) = wrap_result(Ok(5))
let effect: Effect(msg, String) = wrap_result(Error("wrong!"))