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

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

# effect -- abstract parent feature for effects
#
# effect provides a means to perform effectful operations.  Instances
# of effect are installed in the current environment while their code is
# executed.  The code may access the effect via <type>.env.
#
module:public effect (
  # action to be performed, may include code to run while this effect is installed.
  #
  r effect_mode.val
  )
is

  match r
    _ effect_mode.plain   =>
    i effect_mode.inst    => run i.f (_)->unit
    _ effect_mode.repl    => replace
    _ effect_mode.abort   => fuzion.std.panic "abort"
    _ effect_mode.default => default
    _ effect_mode.new     =>

  public mode effect_mode.val is
    match r
      effect_mode.plain => effect_mode.plain
      effect_mode.inst, effect_mode.repl, effect_mode.abort, effect_mode.default, effect_mode.new => effect_mode.repl

  # replace effect in the current context by this
  module replace unit is intrinsic


  # execute code provided in f.call while this effect is installed
  # in the current environment. Return immediately in case abort is
  # called.
  #
  # NYI: uses type parameter T only to simplify C backend
  #
  private abortable(T type : Function unit, f T) unit is intrinsic

  # replace effect in the current context by this and abort current execution
  public abort void is intrinsic

  # set default effect in the current context to this if none is installed
  module default unit is intrinsic

  # execute the code of 'f' in the context of this effect
  #
  module run(R type, f () -> R, def (unit /* NYI: unit type only to distingish 'f' and 'def' in static analysis, remove once 'fz -effects' uses DFA */)->R) R is
    cf := Effect_Call f
    abortable cf
    match cf.res
      nil => def unit
      x R => x


  # abort the current execution and return from the surrounding call to
  # abortable with result == false.
  #
  public return void is abort


  # install this effect and run code.
  #
  # NYI: Currently, there is no direct way to return a result value
  # from the code.
  #
  private use0(
      # the code to be executed within this effect.  This is typically
      # redefined as an argument field of a sub-feature of effect.
      code ()->unit
     )
    pre
      match r
        effect_mode.new => true
        * => false
  is
    abortable (Effect_Call code)



  # has an effect of the given type been installed?
  public type.is_installed(E type) bool is intrinsic



# helper instance for effect.abortable to wrap call to f() into a ()->unit
#
private Effect_Call(redef R type, f () -> R) ref : Function unit is
  res option R := nil
  call =>
    set res := f()


# effect mode is an enum that determines how an instance of effect is used
#
public effect_mode is

  public plain is             # a plain effect, not installed as 'env'
  public repl is              # effect instance replaces previous one in 'env'
  public abort is             # effect instance aborts, jump back to where 'env' was installed
  public default is           # install as default. NYI: remove and use 'new' instead, see io.stdin.fz
  public new is               # a new effect to be installed
  public inst(f ()->unit) is  # install and run 'f'. NYI: remove and use 'new' instead, see io.stdin.fz

  public val : choice plain repl effect_mode.abort default new inst of


public code_effect : effect (effect_mode.inst code)
is

  # the code to be executed within this effect.  This is typically
  # redefined as an argument field of a sub-feature of effect.
  #
  # NYI: Currently, there is no direct way to return a result value
  # from the code.
  #
  public code ()->unit is abstract


# simple_effect provides a simple means to define and use an effect
#
# user-defined effects should inherit from this feature and add
# operations as inner features or fields of function type.
#
# To install this effect to execute a function, simple_effect.use
# can be called.
#
public simple_effect : effect effect_mode.plain
is

  # install this simple_effect and run code
  #
  # In case of an `abort`, `use` returns silently (NYI: maybe this should better
  # return an oucome with the abort wrapped in an error?).
  #
  public use(code ()->unit) unit is
    abortable (Effect_Call code)

  # install this effect and run code that produces a result of
  # type T. panic in case abort is called.
  #
  public go(T type, f ()->T) T is
    fo () -> option T is ()->f()
    r := run fo _->nil
    match r
      nil => panic "*** unexpected abort in {simple_effect.this.type}"
      v T => v