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 mut
#
#  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 : simple_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 := fuzion.sys.misc.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.
  #
  mpanic(msg String) => panic msg


  # common type for mutable data
  #
  private: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 installed effect of type
    # `mutate.this`
    #
    private is_currently_installed =>  my_id = mutate.this.env.id


    # check that this effect is installed and replace it.
    #
    private check_and_replace
    is
      if !is_currently_installed
        mpanic "*** invalid mutate for {mutate.this.type}"
      mutate.this.env.replace


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


    # stop any further mutations of this element
    #
    public close is
      set 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 <-'.
    private mutable_value T
    ) : mutable_element
  is


    # read the current value of this mutable value.
    #
    # If this is open, check that the mutate effect this was created with is still
    # installed 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
    # installed 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 calculcating 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 T get


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


  # create a mutable array.
  #
  module:public array (
         # element type
         public T type,

         # length of the array to create
         public length i32,

         # contents of the array
         module data fuzion.sys.internal_array T,
         _ unit

        ) : Mutable_Array T, mutable_element
  pre
    safety: (length ≥ 0) && (length ≤ data.length)
  is


    # a sequence of all valid indices to access this array. Useful e.g., for
    # `for`-loops:
    #
    #   for i in arr.indices do
    #     say arr[i]
    #
    public indices => 0..length-1


    # is this sequence known to be finite?  For infinite sequences, features like
    # count diverge.
    #
    public redef finite => true


    # get element at given index i
    #
    public redef index [ ] (i i32) T
      pre
        safety: 0 ≤ i < length
    =>
      data[i]


    # set element at given index i to given value o
    #
    public set [ ] (i i32, o T) unit
      pre
        safety: 0 ≤ i < length
    =>
      check_and_replace
      data[i] := o


    # add an element at the end of this array
    #
    public add (o T) unit
    =>
      check_and_replace
      d := if (data.length > length) data
        else
          new_data := fuzion.sys.internal_array_init T (max 8 data.length*2)
          for i in indices do
            new_data[i] := data[i]
          new_data
      d[length] := o
      set data := d
      set length := length+1


    # create immutable array from this
    #
    public redef as_array =>
      array T length (i -> data[i])


    # create a list from this array
    #
    public redef as_list =>
      # since array is mutable,
      # we first copy the elements
      # to an immutable array.
      as_array.as_list


    # initialize one-dimensional mutable array
    #
    public type.new
     (LM type : mutate,

      # length of the array to create
      length i32,

      # initial value for elements
      init T

     ) Mutable_Array T
    =>
      data := fuzion.sys.internal_array_init T length

      for x in data.indices do
        data[x] := init

      LM.env.array T length data unit


    # initialize an empty mutable array of type T
    #
    public type.new (LM type : mutate) Mutable_Array T =>
      LM.env.array T 0 (fuzion.sys.internal_array_init T 0) unit



  # 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 T v


# an interface defining a mutable array
#
# NYI: Remove Sequence parent. Since Sequence is supposed to be immutable,
# we should maybe not pretend to be a Sequence.
#
public Mutable_Array(public T type) ref : Sequence T is

  # add an element at the end of the sequence
  public add(t T) unit => abstract

  # set element at index i to o
  public set [ ] (i i32, o T) unit => abstract

  # the indices of the array
  public indices interval i32 => abstract

  # the length of the array
  public length i32 => abstract


  # is this Sequence known to be array backed? If so, this means that operations
  # like index[] are fast.
  #
  public redef is_array_backed => true


  # count the number of elements in this Sequence.
  #
  # Since we know the exact length, this is redefined to achive O(1) performance instead
  # of O(n) for counting the elements.
  #
  public redef count => length