Fuzion Logo
flang.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.
#
private handles<T,X>(

  # the inner value of this monad
  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)!
  #
  private ar array<T>,

  # action to be taken: plain monad, install or replace?
  redef r effectMode.val
  ) : onewayMonad<X, handles<T,X>> r
is

  # number of handles created
  private count => ar.length


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


  # has one element been created using 'new'?
  #
  hasLast => count > 0


  # return the last handle that was created by 'new'
  #
  last
    pre
      hasLast
   =>
    handle0<T> count-1


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


  # get the value refered to by a given handle
  #
  get (
    # a handle created by 'new'
    h handle0<T>
    )
   =>
    ar[h.x]


  # create a new instance with new value refered to by a given handle
  #
  put (
    # a handle created by 'new'
    h handle0<T>,

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


  # create a new instance with the value refered to by a given handle read and
  # updated.
  #
  update (
    # a handle created by 'new'
    h handle0<T>,

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


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

    # function calculcating the new value from the old value
    f T->T
    )
   =>
    handles<T,X> v (ar.put x (f ar[x])) mode


  infix >>= (f X -> handles<T,X>) => bind<X> f

  bind <B> (f X -> handles<T,B>) handles<T,B> is
    handles<T,B> (f v).v ar effectMode.plain

  return<B> (w B) => handles<T,B> w ar effectMode.plain

# short-hand for creating and installing an empty set of handles of given type.
#
handles<T>(rr ()->unit) =>
  handles<T,unit> unit (array<T> 0 x->do) (effectMode.inst rr)
  unit

# short-hand for creating an empty set of handles of given type.
#
handles_<T> => handles<T,unit> unit (array<T> 0 x->do) effectMode.plain


# short-hand for accessing handles monad for given type in current environment
#
handles<T> =>
  handles_type<T>.installDefault
  handles<T,unit>.env


# handle value created by 'handles.new'
#
private handle0<T>(
  # the index in 'handles.ar'
  private x i32
  )
is

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

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

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


# unit type containing features related to handles but nor requiring an instance
#
handles_type<T> is

  # install default instance of handles
  #
  installDefault unit is
    handles<T,unit> unit (array<T> 0 x->do) effectMode.default
    unit


# create a new handle using the handles instance from the current environment
#
handle<T>(v T) => handles<T>.create v