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
#
# NYI: syntax sugar to read mutable field using
#
#   w := v + 1
#
# instead of
#
#   w := v.get + 1
#
# is not supported yet.
#
public mutate : effect is


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


  # short-hand to access effect type
  #
  # NYI: This does not work yet
  #
  # 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
  #
  id := fzE_unique_id


  # 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

    # id used to verify that mutation made with the same effect
    # the mutable value was created with
    my_id := id


    # check that this effect is the same as the currently instated effect of type
    # `mutate.this`
    #
    is_currently_instated =>
      match mutate.this.type.get_if_instated
        x mutate.this => my_id = x.id
        nil           => false


    # check that this effect is instated and replace it.
    #
    module check_and_replace
    =>
      if !is_currently_instated
        mpanic "*** invalid mutate for {mutate.this.type}"
      mutate.this.env.replace


    # is this element open, i.e., can it be mutated?
    #
    public open => my_id != u64 0


    # stop any further mutations of this element
    #
    public close =>
      set my_id := 0


  # 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 ! mutate.this =>
      if open
        check_and_replace
      mutable_value


    # read the mutable value that is now immutable after it was closed for mutation.
    #
    public val
    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)
      ! mutate.this
    pre
      safety: open
    =>
      check_and_replace
      set mutable_value := to


    # infix operator for put, OCaml/F#-style syntax
    #
    public infix <- (to T) => 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
      )
    =>
      put (f get)


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


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


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



  # install default instance of mutate
  #
  module type.install_default =>
    mutate.default


# short-hand for accessing mut effect in current environment
#
public mut =>
  mutate.install_default
  mutate.env


# create a new mutable value of type T with initial value v
#
public mut(T type, v T) => mut.new v
last changed: 2025-01-23