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

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

# handles2 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.
#
# For performance, this implementation uses mutable state. It can consequently
# only be used as a one-way monad.
#
private:public handles2(
  T, X type,

  # the inner value of this monad
  public v X,

  last_opt option (handle2_0 T),

  # action to be taken: plain monad, install or replace?
  redef r effect_mode.val
  ) : oneway_monad X (handles2 T X) r
is

  # create a new instance with one additional handle
  #
  # the new handle can be accessed by 'result.last'
  #
  public new (
    # initial value refered to by the new handle
    w T
    ) handles2 T X
  post
    result.has_last
  =>
    handles2 T X v (handle2_0 w) mode


  # has one element been created using 'new'?
  #
  public has_last => last_opt??


  # return the last handle that was created by 'new'
  #
  public last
    pre
      has_last
  =>
    last_opt.get


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


  # get the value refered to by a given handle
  #
  public get (
    # a handle created by 'new'
    h handle2_0 T
    )
  =>
    h.get


  # create a new instance with new value refered to by a given handle
  #
  public put (
    # a handle created by 'new'
    h handle2_0 T,

    # the new value to be stored with 'h'
    w T)
  pre
    match mode
      effect_mode.plain   => false  # must be used as oneway_monad only
      effect_mode.default => false
      effect_mode.new     => false
      effect_mode.inst, effect_mode.repl => true
  =>
    h.put w
    handles2 T X v last_opt mode


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

    # function calculcating the new value from the old value
    f T->T
    )
  pre
    match mode
      effect_mode.plain   => false  # must be used as oneway_monad only
      effect_mode.default => false
      effect_mode.new     => false
      effect_mode.inst, effect_mode.repl => true
  =>
    h.put (f h.get)
    handles2 T X v last_opt mode


  public infix >>= (f X -> handles2 T X) => bind X f

  public bind(B type, f X -> handles2 T B) handles2 T B =>
    handles2 T B (f v).v last_opt effect_mode.plain

  public return(B type, w B) => handles2 T B w last_opt effect_mode.plain

# short-hand for creating and installing an empty set of handles2 of given type.
#
handles2(T type, rr ()->unit) =>
  handles2 T unit unit nil (effect_mode.inst rr)
  unit

# short-hand for creating an empty set of handles2 of given type.
#
handles2_(T type) => handles2 T unit unit nil effect_mode.plain


# short-hand for accessing handles monad for given type in current environment
#
handles2(T type) =>
  (handles2_type T).install_default
  (handles2 T unit).env


# handle value created by 'handles2.new'
#
private:public handle2_0(
  T type,

  # get value stored in this handle
  #
  get T
  ) ref
is

  # set value stored in this handle to new_val
  #
  public put(new_val T) is
    set get := new_val
    (handles2 T unit).env.replace

  # update value stored in this handle using f.
  #
  public update(f T->T) =>
    set get := f get
    (handles2 T unit).env.replace
    get

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

  # install default instance of handles2
  #
  install_default unit =>
    handles2 T unit unit nil effect_mode.default
    unit


# create a new handle2 using the handles2 instance from the current environment
#
public handle2(T type, v T) => (handles2 T).create v