Okay. Thank you. It's great to be here for the first time. Really in person. I really
feel.
Yes. You're a regular feature, actually.
Oh, so far only online. It was so sad always, always in my small little room. So here I
am. Want to talk about fusion again like the last years. What I want to talk about here
is about algebraic effects and types and what has changed in the last year in that respect
in the fusion language. Short background to me. I did a lot in compilers, mostly in the
Java area in the past for about 30 years. And also did quite a bit on garbage collection.
So I enjoyed the previous talks here quite a lot. But now to the fusion language, which
is what I'm currently working on. And the main idea behind the fusion language is that
we see more and more language getting more and more overloaded with more and more features
and concepts and new things being introduced. While in fusion, we try to reduce this to
one single concept, which is a fusion feature. And that's basically an abstraction for things
like classes, methods, interfaces, and so on. And instead of having the developer to
choose what to write, whether to write a method or a class or so, have the compiler have the
implementation, make these decisions, what we actually need to implement those features.
And also we see that more and more systems become safe to critical. So we need a simple
language and we need tools to analyze that simple language to ensure the correctness.
So that's what we keep in mind when we work on fusion. Fusion is on GitHub. I don't give
any tutorial in the language or so in this talk. This is all online, so I will just basically
throw you in the water and let you learn to swim bringing fusion code in this talk. But
I hope, I think you will do fine. Fusion is backed by a small company, Tokiba, and now
to this talk. No, before I forget, for those who only came for the fusion stickers, I have
some here, so you can maybe give them around. If you ever once can take one. So now, what
I want to talk about here is basically three points. I start with explaining what algebraic
effects are and how they are implemented or how they are used in fusion. Then I want to
talk a bit about types and how types can be used as first class features and type values
can be used in fusion. And then I bring these two together because that brings a big advantage
when working with algebraic effects. So, what do we need effects for? Fusion, in fusion,
a feature is a pure function, so there is no side effects, no mutation of data by default.
And algebraic effects are used in fusion now to model all the non-functional effects like
state changes, IO, any threat communication or mechanisms like exceptions. So, let me
first define what are algebraic effects. And algebraic effect is actually a set of operations.
You could think of operations like reading data, so performing some IO, getting the time
or doing something like a panic, so causing an error in the application to stop or logging
some data somewhere. Typically, these operations in an algebraic effect model some non-functional
aspect that is kind of orthogonal to the actual computation of your function. These operations
have basically two things that they can mainly do. They could either resume and produce a
result like reading that is successful or they can abort which is like throwing an exception,
you just get out of that, you don't get anything from this. And an effect can be implemented
by different effect handlers. So, one effect type could have different implementations.
And to run code that uses an effect, this code has to be executed while a corresponding
handler for that effect is installed. Now, the effects allow static analysis of the code
that we can analyze what effects any feature has. And we require that for all library code,
the full set of effects is documented in the signature of those features. So, if effects
that are not presented are unexpected are used, that would cause a compile time error.
I start with a small example of a hello world, a hello world feature. We use this exclamation
mark syntax to mark that this code actually uses an effect and the effect or requires
an effect. And that is I O dot out in this case because the library function say requires
that effect. And I run this code now, of course it prints hello world. And if I analyze the
code for the effects that it has, I see that I O dot out is an effect required by this
small example. Now, I want to run the same code, the hello world hasn't changed, using
my own handler. So, I have defined a handler here, which is a feature inherited thing from
can print and it really finds the print operation to print to I O error instead and to replace
the exclamation mark by many exclamation marks. And now to run this, we need to first install
this handler as the I O out handler. The I O out here is just a convenient function that
installs the first handler and executes the code given in the lambda as a second argument.
And when I run this now, of course, I see the print out is the modified string because
we replace the exclamation mark here. And if I analyze this for effects, I now see this
no longer depends on the I O out effect, but the I O error because we have kind of diverted
the code to depend on the other effect. We could also implement a handler that doesn't
do anything, then the hello world executed in that environment would not require any
effect at all anymore. That much to effects. Now, let me talk about types as first class
features in fusion. To make it easier for you to understand what's happening, I give
first an example of generics in Java, where I have a generic method here that takes an
argument A of any generic type T and prints out the value. Doing that in fusion, we have
type parameters, which are actually at the same level as value parameters. So we have
a function with two arguments, T, which is a type, and A, which is a value of that type.
And this is not just syntactic sugar that this looks the same, but it's also internally
the type parameters of our argument features just as the arguments itself. Of course, we
can now, oops, I went a bit further than I wanted. We can now call this function with
two different type parameters and two different value arguments and print these two values.
That's pretty standard for generic for type parametric functions. Fusion uses a lot of
type inferencing. So in such a case where the types are obvious from the arguments,
they don't need to be given. So we have the rule that type parameters always have to
precede the value arguments and they can be left out if they can be inferred. So the code
can be written like this. Then next, we could constrain type parameters. So we could say
in this case, we want a type that must be numeric. And if we have such a constraint,
we could use operations that are only provided by the type, like the plus operator that is
defined on numerics. And if we run this now, we can also output the double value, still
pretty standard for generics. But what we can also do is we can use the type parameter
itself and call features that the type parameter provides, like every type provides its name.
So we can run this code and print those names. And we can go even further with that. And
I want to show you in an implementation of a feature that calculates the sum of a list
of some numeric elements. The implementation of that feature would distinguish the case
of an empty list or a list consisting out of a head and a tail, where we can recursively
calculate the sum. The question now is what do we do in the case of the empty list? In
language like Java, we could have no way to produce any value here. What we can do is
we can call a feature that is defined in the type numeric and redefined by all concrete
implementations to provide the zero value for that actual numeric type. So numeric itself
is defined as a feature with its corresponding type defining zero as an instance of exactly
this type. And then something like an i32, 32-bit integer, defines an implementation
of type.zero to return the integer zero. And we can now use that function to print the
sums of different lists here. And when I do this, we have a list of floating point values,
a list of fractions. We have an empty list of floating point values and an empty list
of fractions, so we get the corresponding zero values from the types of the corresponding
types. So that much to types. And now coming back to effects, I want to use these types
and type parameters to give names to effects or to reference user-defined effects. And
I'll give you an example using a code that creates a linked ring, so a ring structure.
To create a ring structure with references, you need mutation because at some point you
have to close the ring. So that code is mutable, it's not easily pure. Then, so we will see
that this depends on the mutate effect. And then we want to reimplement this or extend
this to use local mutable state or local mutability to make this function pure. So I start by
code to create a ring that uses the mutate effect. You don't need to understand the whole
code, the important thing is that every element in that ring has a pointer to the next element
and there's a reference to the very last, because if you extend the ring, you have to
update the next of the last element in the ring to point to the newly added element in
the ring. And here, for next, we create a mutable value, which is done by a base library
function mut, which used the mutate effect. And to update the next, we use this arrow
operation to update that. Now we create a small demo, we create a ring with elements
ABC and then we run 10 times through the ring to print them. So we do that, we see that
it circles around in that ring. But if you now analyze this for effects, what we see
is that we have a mutate effect used by the code. There's lots of other effects as well,
the out effect, because we print something, but there's also error handling in the library
code that shows up here. But what I'm interested in is here is now, this has the mutate effect,
because the code mutates the next element while building the ring. And now we want to
get rid of this mutability in the code, I know, I think I'll make it in five minutes.
And the way to do this is we define a local instance of the mutate effect. And to do that,
I first need a bit more space in the code. And I'll add a type parameter M of type mutate
to the code here and also pass this on on calls and on types of ring used in here. And
now when we create an instance of such a mutable reference to the next element, instead, we
take the instance of the mutate effect M, which we got as a parameter here, from the
current environment. The syntax we use in fusion there is type dot n, which is the effect
from the environment, plus dot and the operation new create a new mutable variable. And now
we can define our own mutate effect. Here mm is the local mutate effect defined here,
which is just inheriting from mutate. And is nothing follows after the is, so it doesn't
do anything special, it is just a new sub feature in inheriting from mutate. So it basically
only has the purpose of giving a new name to this is my local mutate here. And now we
can pass this sub instance of mutate or the sub type of mutate to the ring here, which
means that all the mutable values that are created are created locally to the mm to our
own mutate effect. Now we still have to create this effect and execute the code within the
context of this effect, and this happens in the bottom here. So we create an instance
of mm and use it to execute the demo code. And that means that the m dot f nth call
here will then take the instance of mm from the current environment to create the new
mutable value. And when we run the demo, the same output, if we analyze it for effects
now, we see it's the same effect. Apart from the mutate is gone, because the mutate is
completely local here. So we can create code that locally to perform some calculation creates
mutable data and mutates data structures. But the result is a pure function anyway,
because the mutation is only done locally. So I'm coming to the end. The fusion of status
is still under developing. It's a very experimental language, but the language definition is slowly
getting more stable. There's still a lot of work on the base library that is ongoing.
The current implementation has two back ends. One is a very slow interpreter running on
the JVM, and the other is a C back end, which also used the beam demo visor garbage collector,
which we just learned about as a garbage collector right now, but I would like to have a precise
garbage collector and add a lot there as well. And basic analysis tools like you've seen
for the effects here are available. And yes, those who remember Ellie might wonder who
is disturbing me now from while I'm working on this is Felix. That's it. That's coming
to the end. So maybe one more sentence. I hope I could show you that algebraic effects
and types as first class features are something that complements one another pretty well.
It helps to create code, then encapsulates non-functional effects, and yeah, that makes
it possible to work with this and work even with code that is not pure, but to manage
this in a nice way. You find some links here to resources related to fusion. We are happy
for everyone who gets involved. Please have a look. Join us. We are a small team currently
from three working on this. We can, there should be more. Yeah. Thank you very much.
Can I pick? Yeah. You think you were first? So earlier you said that a particular type
can influence numeric, that all terminology you used, but is it a numeric interface? And
then you were able to say A plus B. If you didn't say that influence numeric, what would
happen if you tried to compile that program? The question was if a particular type would
not implement numeric, and you would use the plus in there, what would happen then?
You would get a compile time error. It's completely strict typing. So if you call a function that
requires a numeric type parameter, and you call it with say a string, what string happens
to have a plus, but not the numeric plus, you will get an error that type, the actual
type parameter is not compatible to the type constrained in the call feature, so that will
not be an example. You were converting the value to string in order to print it. Is
the string operation implied to be present for every value? There's a two string operation
in our any, which is the parent of any feature. So a two string is always available, yes.
It's not very helpful if you don't define anything, because you just, it's a second,
the next speaker setup, which I'm contributing to the language called NIM, which also have
my effect types, and ad hoc, more important, generic call. One problem that I've seen in
combining these features is that when you have generic call, you often don't, the concrete
instantiations may trigger different effects. So how do you approach this, and syntactically
or semi-events, semi-table language? The question was that actual code can actually
trigger all sorts of different actual effects at runtime, so you could have, one example
I think of, you could have a function converting an object to a string that maybe performs
some logging, and some code printing that value would not expect that. My answer to
that is, that must be part of the static analysis. We need to analyze the whole application
and see what is happening there. Library functions can do this to a certain extent,
but they cannot predict if you have a dynamic value coming in, what the actual type will
be, so we need a whole program analysis in the end there. Do we have time for one more?
Yeah, another approach to have pure functional function and side effects is, like Haskell
can do this. So I find it really interesting that with this language, it's possible to
get rid of the effect of the algebraic effect. Is that like a decision, or do other languages
like Unison, for example, also use algebraic effects, also have these features, and isn't
that also like kind of, is it like on purpose, or is it like, what are the pros and cons of
it? So the question was, in Haskell you have monads, which have a similar role like the
effects, but you have them always explicitly, you have to carry them around and mark them.
And the answer is here, this is actually, it's on purpose, we don't want to have the hassle
of wrapping everything into a monad and carrying it around all the time. So the idea is to get
rid of this as much as possible without losing the information you get from the effects.
Time's up.
Okay. Thank you for all the questions.