State
State[S] provides mutable state scoped to a computation. You can read and update a value of type S without passing it around explicitly.
Basic usage
import purelogic.*
case class Counter(value: Int)
def increment(using State[Counter]): Unit = {
val current = get(_.value)
set(Counter(current + 1))
}
val (finalState, result) = State(Counter(0)) {
increment
increment
increment
get
}
// finalState: Counter(3)
// result: Counter(3)Functions
get
Returns the current state:
val counter: Counter = getYou can also pass a projection function to extract a field directly:
val value: Int = get(_.value)set
Replaces the state with a new value:
set(Counter(10))update
Modifies the state using a function:
update(c => Counter(c.value + 1))updateAndGet
Updates the state and returns the new value:
val newState: Counter = updateAndGet(c => Counter(c.value + 1))getAndSet
Returns the old state and replaces it:
val oldState: Counter = getAndSet(Counter(0))getAndUpdate
Returns the old state and modifies it:
val oldState: Counter = getAndUpdate(c => Counter(c.value + 1))modify
Computes a return value and a new state from the current state in one step:
val previous: Counter = modify { c =>
(c, Counter(c.value + 1))
}localState
Runs a block with a modified state. The original state is restored after the block completes:
def process(using State[Counter]): Int = {
set(Counter(5))
localState(c => Counter(c.value * 10)) {
get(_.value) // 50
}
get(_.value) // 5
}focusState
Runs a block that operates on a subset of the state. Reads and writes to the focused state are reflected in the outer state:
case class App(counter: Counter, name: String)
def increment(using State[Counter]): Unit = {
update(c => Counter(c.value + 1))
}
def process(using State[App]): Unit = {
focusState(_.counter)((app, c) => app.copy(counter = c)) {
increment // operates on Counter, but updates App
}
}Running
State(initial)(body) returns a tuple of the final state and the result:
val (finalState: S, result: A) = State(initialState) {
myProgram
}