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

mutate.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 mutate
#
#  Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------

# mutate -- an effect that permits creation and mutation of mutable values.
#
# This effect is typically used to work with mutable values. You can create
# a mutable value as follows
#
#     v := mutate.env.new i32 42
#
# and then modify it using
#
#     v <- 666
#
# To read it, call 'get' as in
#
#     say "v is $(v.get)"
#
# Convenience feature 'mut' and type inference allow the creation to be
# written as
#
#     v := mut 42
#
# syntax sugar to read mutable field using
#
#     w := v + 1
#
# instead of
#
#     w := v.get + 1
#
public mutate : linear_effect is


  # does this effect support abort?
  public redef abortable bool => false


  # shorthand to access effect type
  #
  module M := mutate.this.type


  # an id used for runtime checks to verify that mutation made with the same effect
  # the mutable value was created with
  #
  module id := fzE_unique_id


  # NYI: UNDER DEVELOPMENT: thread id used to make sure this effect is
  # not teleported to another thread and used there to mutate data.
  #
  # This should become more sophisticated with mutate effects that support
  # synchronization in a way that access by several threads is possible.
  #
  module tid =>
    tid0.or_else ()->
        res := concur.Threads.env.current
        set tid0 := res
        res


  # the thread this mutate was created with
  #
  # NYI: UNDER DEVELOPMENT: Since effect order is currently not enforced during the
  # big bang singularity, we set this lazily.
  #
  tid0 option concur.Thread := if concur.Threads.is_instated then concur.Threads.env.current
                                                             else nil


  # thread that currently has exclusive access to this mutate. This is the creating thread
  # for a mutate that is not sharable by threads. Otherwise, this is `nil` if no thread
  # currently has exclusive access, or the thread that currently does have exclusive
  # access.
  #
  module exclusive_thread option concur.Thread => tid


  # check if the current thread is permitted to access mutable state managed by mutate.this
  # exclusively.
  #
  # For a single-thread mutate, this is usually the case for the same thread this mutate was
  # created in.  Furthermore, multi-thread mutate implementations may ensure exclusive
  # access to other threads via redefining the `exclusive` features.
  #
  public is_exclusive bool
  =>
    M.get_if_instated >>? (x -> id = x.id && x.exclusive_thread=concur.Threads.env.current)


  # panic operation to be used in case of a severe problem.  This is redefined within the
  # base lib to make sure fundamental uses of local mutability do not cause dependency
  # on the panic effect.
  #
  module mpanic(msg String) => panic msg


  # common type for mutable data
  #
  module:public mutable_element is


    # check that this effect is the same as the currently instated effect of type
    # `mutate.this`
    #
    is_currently_instated =>
      open && is_this_mutate_instated_and_usable


    # check that this effect is instated and replace it.
    #
    module check_and_replace
    =>
      if !is_exclusive
        mpanic "*** invalid mutate for '$(M.name)'"
      mutate.this.env.replace


    # is this element open, i.e., can it be mutated?
    #
    public open bool := true


    # stop any further mutations of this element
    #
    public close unit
    pre
      safety: is_exclusive
    =>
      set open := false


  # create a new mutable value with the given initial value and update the
  # 'mutate' effect in the current environment
  #
  public new (
    public T type,

    # initial value, will be updated by 'put' or 'infix <-'.
    mutable_value T
    ) : mutable_element,
        auto_unwrap T mutate
  is


    # read the current value of this mutable value.
    #
    # If this is open, check that the mutate effect this was created with is still
    # instated in the current environment.
    #
    public get T ! mutate.this
      pre
        safety: !open || is_exclusive
    =>
      if open
        check_and_replace
      mutable_value


    # read the mutable value that is now immutable after it was closed for mutation.
    #
    public val T
    pre
      safety: !open
    =>
      mutable_value


    # update mutable field with new value
    #
    # Check that the mutate effect this was created with is still
    # instated in the current environment.
    #
    public put (
      # the new value to be stored with 'h'
      to T) unit
      ! mutate.this
    pre
      safety: open
    =>
      check_and_replace
      set mutable_value := to


    # infix operator for put, OCaml/F#-style syntax
    #
    public infix <- (to T) unit => put to


    # update mutable field using a function of the old value
    #
    public update (
      # function calculating the new value from the old value
      f T->T
      ) unit
    =>
      put (f get)


    # creates a copy of the mutable field
    #
    public copy new T =>
      new get


    # unwrap this mutable value
    #
    public redef unwrap T ! mutate.this => get


    # returns `as_string` of the current value
    #
    public redef as_string String => $get


  # is instance `mutate.this` instated for type `mutate.this` and, if `mutate.this` does
  # not support use in several threads, check we are running in the thread `mutate.this`
  # was created in.
  #
  # This is used a pre-condition for `exclusive` and `exclusive_when`.
  #
  # Note that this is redefined in `blocking_mutate_using_clock` since it does not
  # require exclusivity there, but it is sufficient if that is instated.
  #
  public is_this_mutate_instated_and_usable bool
  =>
    is_exclusive


  # perform given code with exclusive access to the mutable values created with
  # this instance of `mutate`.
  #
  public exclusive(R type, F type: ()->R, code F) R
  pre
    safety: is_this_mutate_instated_and_usable
  =>
    code()


  # wait for the given condition to become true by a mutation of the underlying data.
  #
  # In case this mutate is not a multi-thread implementation and `condition()` is `false`,
  # this will `panic`.
  #
  public wait(F type: ()->bool, condition F) unit
  pre
    safety: is_exclusive
  post
    debug: condition()
  =>
    if !condition() then
      panic "mutate.wait on '$(F.name)' would wait forever"


  # exclusively wait for the given condition to hold and then perform given code with
  # exclusive access to the mutable values created with this instance of `mutate`.
  #
  public exclusive_when(R type,
                        C type: ()->bool,
                        F type: ()->R,
                        condition C,
                        code F
                        ) R
  pre
    safety: is_this_mutate_instated_and_usable
  =>
    mutate.this.env.exclusive ()->
      mutate.this.env.wait condition
      code()


  # default implementation of mutate
  #
  # this will get instated automatically at startup
  #
  public redef fixed type.default_value option mutate =>
    mutate


# create a new mutable value of type T with initial value v
#
public mut(T type, v T) mutate.new T ! mutate
=>
  mutate.env.new v

last changed: 2026-08-14