Fuzion Logo
fuzion-lang.dev — The Fuzion Language Portal

Effects

A pure function is a function whose result depends only on its arguments. There must be no dependency on state, io, time, etc. for a function to be pure. Pure functions have very nice features such as being thread-safe and very easy for optimizing compilers. Nevertheless, pure functions alone are useless since all useful programs interact with some input to produce some output.

Effects are a means to model non-functional aspects of an otherwise pure function. In Fuzion, effects additionally provide a means to change the implementation of an effect used by a function. For example, a function might perform logging using an effect. Then, the caller of this function itself requires this effect unless it provides a handler for the logging effect.

Effectful Hello-World

As an introduction, let us implement a simple Hello World! using a greeter effect that provides an operation greet:

# Greeter is an effect providing on operation 'greet'
Greeter ref : effect is
{
# greet takes a name and greets that name
greet(x String) unit => abstract
}
# our main code uses greeter effect
main ! Greeter =>
{
Greeter.env.greet "World";
}
# a Greeter that prints to io.out
g : Greeter is
{
redef greet(x String) => { say "Hello, $x!" }
}
# run main using g
Greeter.instate g main;
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Note that the effects listed when the Fuzion compiler analyzes this program (click Effects!) is io.out, not greeter since greeter is installed explicitly, but the installation uses io.out.

If we run main directly without setting a greeting effect, we will get an error:

# Greeter is an effect providing on operation 'greet'
Greeter ref : effect is
{
# greet takes a name and greets that name
greet(x String) unit => abstract
}
# our main code uses greeter effect
main ! Greeter =>
{
Greeter.env.greet "World"
}
# a Greeter that prints to io.out
g : Greeter is
{
redef greet(x String) => { say "Hello, $x!"; }
}
# run main without setting Greeter.env, will cause an error
main();
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

In this case, the analysis for required effects (click Effects!) shows greeter since this effect is required to run this code.

To give a glimpse of the power of effects, we now run a slightly more complex main feature on a list of different implementations of the greeter effect. First, the same as before. Next, an implementation that prints to the standard error stream instead. Then, one that is just silent and does not greet at all. And finally, one that performs only the first greeting and then returns, i.e., it stops any further calculation within main and directly returns from the call to use.

# greeter is an effect providing on operation 'greet'
Greeter ref : effect is
{
# greet takes a name and greets that name
greet(x String) unit => abstract;
}
# our main code uses greeter effect
main ! Greeter =>
{
Greeter.env.greet("Alice");
Greeter.env.greet("Bob");
Greeter.env.greet("Charlie");
}
# create instances of greeter
gs := [("Hello-greeter" , ref : Greeter
{
redef greet(x String) => { say("Hello, $x!"); }
}),
("Hi-greeter on io.err", ref : Greeter
{
redef greet(x String) => { io.err.println("Hi $x!"); }
}),
("silent greeter" , ref : Greeter
{
redef greet(x String) => { }
}),
("return greeter" , ref : Greeter
{
redef greet(x String) => { say "First $x"; Greeter.abort; }
})]
# run main using all greeters in gs
for ng in gs do
{
(n,g) := ng;
# run main using g
say("using $n");
Greeter.instate(unit, g, main, (_->unit));
say("done $n.");
say("");
}
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Coroutines using Effects

Here is a small example defining a generator effect gen T with an operation yield. The effect is then used in a feature that traverses a list to yield all the elements of that list.

# example inspired by yield.kk sample in koka taken from
# https://github.com/koka-lang/koka/blob/master/samples/handlers/yield.kk
# define a generator effect with a yield operation
gen(T type,
yield T->unit # yield is called by code to yield values
) : effect is
{
# traverse a Sequence and yield the elements
Sequence.traverse() unit =>
{
match (as_list)
{
c Cons => (gen T).env.yield c.head; c.tail.traverse
nil =>
}
}
}
# bind the yield operation dynamically
(gen(i32, (i) -> say "yielded $i")).instate_self (()->
[0,8,15].traverse());
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Using State Effect to Count

The next example also traverses a list, but this time to count the number of elements. For this, a state effect is used that is incremented for each element of the list that is traversed.

# traverse a list and count the elements in state(i32)
list.count_elements unit =>
{
match list.this
{
c Cons => _ := state_modify(i32, (n)->n+1); c.tail.count_elements
nil =>
}
}
# install state i32 and call list.count_elements
list.count_els =>
{
state(i32, i32, 0, () ->
{
count_elements();
state_get(i32);
});
}
l := [0,8,15,47,11].as_list();
say("number of elements is {l.count_els}");
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
last changed: 2024-06-28