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.default => default
_ effect_mode.new =>
public mode effect_mode.val =>
match r
effect_mode.plain => effect_mode.plain
effect_mode.inst, effect_mode.repl, effect_mode.default, effect_mode.new => effect_mode.repl
# replace effect in the current context by this
module replace unit => 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 => intrinsic
# replace effect in the current context by this and abort current execution
public abort void
pre
safety: abortable
=>
abort0
# Intrinsic version of abort.
#
# NYI: precondition does not seem to work for an intrinsic yet, causes test failure e.g., in tests/local_mutate
# so we use cannot make `abort` intrinsic.
private abort0 void
=>
intrinsic
# does this effect support abort?
#
# Redefining this to return `false` helps to detect unexptected calls to
# `abort` at runtime and ensure that the static analysis finds that there
# code executed with this effect will always return normally and produce
# a result. This is used, e.g, in `mutate` to avoid static analysis
# reporting `panic` as an effect of the use of a local mutate instance.
#
public abortable => true
# set default effect in the current context to this if none is installed
module default unit => intrinsic
# execute the code of 'f' in the context of this effect
#
module run(R type, f () -> R, def ()->R) R =>
cf := Effect_Call f
abortable cf
match cf.res
nil => def()
x R => x
# abort the current execution and return from the surrounding call to
# abortable with result == false.
#
public return void
pre
safety: abortable
=>
abort
# has an effect of the given type been installed?
public type.is_installed(E type) bool => intrinsic_constructor
# helper instance for effect.abortable to wrap call to f() into a ()->unit
#
private Effect_Call(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 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 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 => 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 =>
cf := Effect_Call code
abortable cf
# ignore cf.res
# 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 =>
cf := Effect_Call f
abortable cf
match cf.res
nil => msg := "*** unexpected abort in {simple_effect.this.type}"
if abortable
panic msg
else
# the user should not be able to produce this state: since the effect is
# not abortable cf can only return with a result. So we do a low-level
# panic that does not show up as a used effect:
fuzion.std.panic msg
x T => x
last changed: 2024-03-07