Fuzion Logo
fuzion-lang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

state.fz


# This file is part of the Fuzion language implementation.
#
# The Fuzion language implementation is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3 of the License.
#
# The Fuzion language implementation is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with The
# Fuzion language implementation.  If not, see <https://www.gnu.org/licenses/>.


# -----------------------------------------------------------------------
#
#  Tokiwa Software GmbH, Germany
#
#  Source code of Fuzion standard library feature state
#
#  Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------

# state -- represent a state using a monad
#
# this can be used both as plain or as a oneway monad to store a state
# in a way orthogonal to the actual computation.
#
# use as follows to store data of type `u8` for purpose `demo`:
#
#      # first, declare a type that holds a value of `u8` that describes the purpose
#      #
#      demo(contents u8) is
#
#      # then, create an instate an instance of `state` with initial value `23`
#      #
#      state (demo 23) ! ()->
#
#        # now, we can access the value using `state_get demo`
#        # or, to unwrap it `state_get demo .contents`
#        #
#        say "state value is {state_get demo .contents}"
#
#        # we can now modify the value, e.g, doubling it via
#        #
#        new_demo := state_modify demo (x -> demo x.contents*2)
#        say "state value is {new_demo.get.contents}"
#        say "state value is {state_get demo .contents}"
#
# note that access to the state value is possible in any code executed while the state
# is instated, also, e.g., in lambdas that are passed to a call and then called
# further down the call chain.
#
# The wrapper type, `demo` in the example, is not strictly required, we could use
# just `u8` instead.  However, this would permit unrelated code to read and modify
# the value using `state_get u8`, while the wrapper type `demo` restricts this to
# code for which `demo` is visible.  To avoid accidental and intentional (malicious)
# accesses to state effects, we need our own local type!
#
public state(

  # types of contained and the state value
  public T, S type,

  # the contained value
  public val T,

  # the current state
  public get S,

  # `plain` monad or effect to be `inst`alled or `repl`aced by new value?
  mode oneway_monad_mode.val
  ) : oneway_monad T (state T S) mode
is

  # monadic operator
  #
  public redef infix >>= (f T -> state T S) state T S => state.this.bind f


  # monadic operator
  #
  # Same as non-generic >>=, but also maps to a different type B.
  #
  public bind (B type, f T -> state B S) state B S =>
    f val


  # return function
  #
  public return (a T) state T S => state a get mode.next


  # map this using f
  #
  public map (B type, f T -> B) state B S =>
    state (f val) get mode.next


  # modify the state, leaving the contents unchanged
  #
  public modify (f S -> S) state T S =>
    state val (f get) mode.next


  # set state to new, leaving the contents unchanged
  #
  public put (new S) state T S =>
    state val new mode.next


  # converts option to a string
  #
  # returns the result of $T for an option containing an instance
  # of T, alternatively returns $nil for an option that is nil.
  #
  public redef as_string String =>
    "$val ($get)"


# state with 1 argument is short hand for a state containing unit and
# initial_value
#
public state(S type, initial_value S) state unit S => state unit initial_value oneway_monad_mode.inst


# install new state monad for type S and run r within that state monad
#
# return result of r.
#
public state(S, R type, initial_value S, r ()->R) R =>
  res option R := nil
  (state unit S) <- (state unit S unit initial_value oneway_monad_mode.inst) ! (()->set res := r.call)
  res.or_panic


# shorthand for accessing state monad for given type in current environment
#
public state_of(S type) state unit S => (state unit S).env


# shorthand for getting state monad for given type in current environment
#
public state_get(S type) S => (state_of S).get


# shorthand for modifying state monad for given type in current environment
#
public state_modify(S type, f S->S) state unit S => (state_of S).modify f


# shorthand for setting state monad for given type in current environment
#
public state_put(S type, new S) state unit S => (state_of S).put new

last changed: 2026-05-12