Current section
Files
Jump to
Current section
Files
src/smut.gleam
import smut/types.{type SmutError, type TableHandle}
@target(erlang)
import internal/erl_impl as impl
/// Create a table to hold shared, mutable state values and return a
/// handle to it.
///
/// You should call this function as early in your program as possible,
/// preferably in your main function, and pass this handle around.
pub fn init() -> TableHandle {
impl.init()
}
/// Represents a handle to an individual shared, mutable value of type `a`.
pub type State(a) =
impl.State(a)
/// Insert a new `initial_val` of type `a` into the global state table,
/// returning a handle for reads, writes, and updates.
pub fn new(table: TableHandle, initial_val: a) -> Result(State(a), SmutError) {
impl.new(table, initial_val)
}
@target(erlang)
/// Change the default retrieval timeout (for retrievals that require a
/// timeout) for this particular value to `ms` milliseconds.
///
/// The default timeout is otherwise 5000 ms.
pub fn with_timeout(state: State(a), ms: Int) -> State(a) {
impl.with_timeout(state, ms)
}
/// Retrieve the current value of `state`.
///
/// Global state, in general, is problematic. I have done my (admittedly
/// non-expert) best to ensure this library runs smoothly, but if anything
/// goes wrong, this function will return `Error(Nil)`. It does not return
/// a more informative error message, because if something _has_ gone wrong,
/// your program, which relies on this state, is probably hosed.
///
/// (In future versions, I may attempt to provide some facilities for
/// attempting to recover from certain kinds of errors, but that's all
/// very contingent.)
pub fn get(state: State(a)) -> Result(a, Nil) {
impl.get(state)
}
/// Replace the current value of `state` with the given `new_val`.
///
/// The majority of the time, you will probably want to use one of
///
/// * `update()`
/// * `update_and_get()`
/// * `get_and_update()`
///
/// instead. An operation like
///
/// ```gleam
/// let assert Ok(cur_val) = smut.get(state)
/// let new_val = update_state(cur_val)
/// smut.set(state, new_val)
/// ```
/// is not guaranteed to be atomic; another process could futz with the
/// value of `state` between the call to `smut.get()` and `smut.set()`.
/// The `update` family of functions ensure "atomic" updates.
///
pub fn set(new_val: a, state: State(a)) -> Nil {
impl.set(new_val, state)
}
/// Update the current value of `state` `with` the given update function.
///
/// This operation is guaranteed to be "atomic"; that is, no other process
/// can affect the value of `state` between its retrieval and replacement;
/// its value immediately after the function call will be the value returned
/// by `with`.
pub fn update(with: fn(a) -> a, state: State(a)) -> Nil {
impl.update(with, state)
}
/// Update the value of `state` `with` the given function, then return the
/// new value.
///
/// ```gleam
/// // Our updater function, which we define ahead of time to make the
/// // call to update_and_get more readable.
/// let f = fn(n) { n + 2 }
///
/// let assert Ok(val) = smut.get(state)
/// echo val
/// // 8001
/// let assert Ok(new_val) = smut.update_and_get(f, state)
/// echo new_val
/// // Guaranteed to be 8003 as long as no other process changed `state`
/// // between the calls to smut.get and smut.update_and_get.
/// ```
pub fn update_and_get(with: fn(a) -> a, state: State(a)) -> Result(a, Nil) {
impl.update_and_retrieve(with, state)
}
/// Return the value of `state` and _then_ update it `with` the given
/// function.
///
/// This operation is guaranteed to be "atomic"; that is, no other process
/// can affect the value of `state` between its retrieval and replacement.
/// ```gleam
/// // Our updater function, which we define ahead of time to make the
/// // call to get_and_update more readable.
/// let f = fn(n) { n + 2 }
///
/// let assert Ok(val) = smut.get(state)
/// echo val
/// // 8001
/// let assert Ok(same_val) = smut.get_and_update(f, state)
/// echo same_val
/// // Guaranteed to be 8001 as long as no other process changed `state`
/// // between the calls to smut.get and smut.get_and_update.
/// let assert Ok(new_val) = smut.get(state)
/// // Guaranteed to be 8003 as long as no other process changed `state`
/// // between the calls to smut.get_and_update and the second call
/// // to smut.get.
/// ```
pub fn get_and_update(with: fn(a) -> a, state: State(a)) -> Result(a, Nil) {
impl.retrieve_and_update(with, state)
}