import gleam/erlang/process.{type Subject} type Msg(state) { Assign(state) Update(Subject(state), fn(state) -> state) Retrieve(Subject(state)) } pub opaque type State(state) { Handle(Subject(Msg(state))) } fn run(state: state, pipe: Subject(Msg(state))) -> Nil { case process.receive_forever(pipe) { Assign(new_state) -> run(new_state, pipe) Update(return_pipe, with) -> { process.send(return_pipe, state) with(state) |> run(pipe) } Retrieve(return_pipe) -> { process.send(return_pipe, state) run(state, pipe) } } } pub fn new(initial_state: state) -> State(state) { let back_channel: Subject(Subject(Msg(state))) = process.new_subject() process.spawn(fn() { let pipe: Subject(Msg(state)) = process.new_subject() process.send(back_channel, pipe) run(initial_state, pipe) }) let pipe = process.receive_forever(back_channel) Handle(pipe) } pub fn set(new: state, handle: State(state)) { let Handle(send) = handle process.send(send, Assign(new)) } pub fn get(handle: State(state)) -> state { let return: Subject(state) = process.new_subject() let Handle(send) = handle process.send(send, Retrieve(return)) process.receive_forever(return) }