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) }