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

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

# handles provide a means to create handles that refer to update-able
# cells.
#
# handles is a state monad.  It provides features to create several
# handles that refer to modifiable value and features to `get`, `put` or
# `update` this value.
#
# an example of using handles is shown below, handle (singular) is an
# alias to initialize a new handle with the given initial value. `handle_id T`
# is the type that allows passing around handles.
#
#     ex_handles is
#       hdl := handle 42
#
#       say hdl.get
#
#       x(h handle_id i32) unit =>
#         b := hdl.put 17
#         say b
#         say hdl.get
#
#       x hdl
#
#       say hdl.get
#
# the example also shows the difference between handles and the mutate effect:
# while both provide a way to store and update a mutable value, a handle can be
# passed around between features. meanwhile, mutables are not a type that can be
# given to other features, only values can. handles essentially provide an
# abstraction of pointers, implemented natively in Fuzion.
#
# NYI: CLEANUP: This is currently used as an example for the equivalence of an
# effect and a monad in the [Fuzion design]("https://fuzion-lang.dev/design/monadic_lifting).
# We might remove `oneway_monad` from the base lib if this is needed only for the
# design pages and add code for a `handles` monad to the example code where this
# is currently used as a monad.
#
private:public handles(
  T, X type,

  # the inner value of this monad
  public v X,

  # array containing values stored for the handles
  #
  # NYI: As soon as one-way monads are enforced, this array can be implemented
  # using marray, reducing the overhead of an update from O(count) to O(1)!
  #
  ar array T,

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

  # number of handles created
  count => ar.length


  # create a new instance with one additional handle
  #
  # the new handle can be accessed by `result.last`
  #
  public new (
    # initial value referred to by the new handle
    w T
    ) handles T X
  post
    debug: result.has_last
  =>
    na := array count+1 (i -> if (i < count) ar[i] else w)
    handles v na mode.next


  # has one element been created using `new`?
  #
  public has_last bool => count > 0


  # return the last handle that was created by `new`
  #
  public last handle_id T
    pre
      debug: has_last
  =>
    handle_id T count-1


  # a one-way feature to create a new handle and update the monad
  # in the current environment
  #
  /* env */
  public create (
    # initial value referred to by the new handle
    w T
    ) handle_id T
  =>
    (new w).last


  # get the value referred to by a given handle
  #
  public get (
    # a handle created by `new`
    h handle_id T
    ) T
  =>
    ar[h.x]


  # create a new instance with new value referred to by a given handle
  #
  public put (
    # a handle created by `new`
    h handle_id T,

    # the new value to be stored with `h`
    w T) handles T X
  =>
    handles v (ar.put h.x w) mode.next


  # create a new instance with the value referred to by a given handle read and
  # updated.
  #
  public update (
    # a handle created by `new`
    h handle_id T,

    # function calculating the new value from the old value
    f T->T
    ) unit
  =>
    update0 h.x f


  # create a new instance with the value referred to by a given handle read and
  # updated.
  #
  update0 (
    # a handle created by `new`
    x i32,

    # function calculating the new value from the old value
    f T->T
    )
  =>
    _ := handles v (ar.put x (f ar[x])) mode.next


  public redef infix >>= (f X -> handles T X) handles T X => bind X f

  public bind(B type, f X -> handles T B) handles T B =>
    handles (f v).v ar oneway_monad_mode.plain

  public return( B type, w B) handles T B => handles w ar oneway_monad_mode.plain

# shorthand for creating and installing an empty set of handles of given type.
#
public handles(T type, rr ()->unit) unit =>
  (handles unit (array T 0 x->do) oneway_monad_mode.inst) ! rr

# shorthand for creating an empty set of handles of given type.
#
public handles_(T type) handles T unit => handles unit (array T 0 x->do) oneway_monad_mode.plain


# shorthand for accessing handles monad for given type in current environment
#
public handles(T type) handles T unit =>
  (handles_type T).install_default
  (handles T unit).env


# type of the reference to the cell that holds a value of type T which can be updated
# using the handles interface
#
private:public handle_id(
  T type,
  # the index in `handles.ar`
  x i32
  )
is

  # set value stored in this handle to new_x
  #
  public put(new_x T) is
    _ := update old_x->new_x

  # update value stored in this handle using f.
  #
  public update(f T->T) T =>
    (handles T).update0 x f
    get

  # get value stored in this handle
  #
  public get T =>
    (handles T).ar[x]


# unit type containing features related to handles but not requiring an instance
#
handles_type(T type) is

  # install default instance of handles
  #
  install_default unit =>
    (handles unit (array T 0 x->do) oneway_monad_mode.inst).default


# initializes a new handle with the given initial value
#
# this uses the handles instance from the current environment
#
public handle(T type, v T) handle_id T => (handles T).create v

last changed: 2026-03-12