[00:00.000 --> 00:13.260] A really cool, interesting project, a new language being presented by Fridtjof called [00:13.260 --> 00:18.280] Fusion on the OpenJDK. [00:18.280 --> 00:23.480] Final session of the day, thank you so much for being with us, some of you, all day. [00:23.480 --> 00:26.640] So let's start with Fusion or end with Fusion. [00:26.640 --> 00:31.280] OK, thank you for staying so long, thank you for Gertjans, he just downloaded the latest [00:31.280 --> 00:35.680] version of my slide deck and was a bit shocked that it's almost one hundred slides and only [00:35.680 --> 00:39.680] have twenty minutes left, so let's see how that will work out. [00:39.680 --> 00:48.960] For those who came for the Fusion stickers, please pass them around and take one. [00:48.960 --> 00:56.600] So Fusion, a new language, and it's different, it's more from a Java perspective. [00:56.600 --> 01:01.920] But there's some overlap, you will see. [01:01.920 --> 01:06.720] So basically the idea, the original idea of Fusion was to have something like a simpler [01:06.720 --> 01:14.280] Java to simplify Java's features into Fusion features. [01:14.280 --> 01:19.400] Bit of my background, I did work on compilers for about thirty years, a big part of that [01:19.400 --> 01:25.120] working on read time Java implementations, read time garbage collection and so on. [01:25.120 --> 01:33.920] Start with motivating a quote from John Bacchus, the inventor of Fortran, who worked a lot [01:33.920 --> 01:39.000] on functional programming but was very disappointed because his work on functional programming [01:39.000 --> 01:44.480] basically failed and would likely always fail because functional programming is easy to [01:44.480 --> 01:51.040] do hard things but incredibly difficult to do simple things. [01:51.040 --> 01:57.160] Fusion has evolved into a functional language and I think, I hope, I find ways to even make [01:57.160 --> 02:00.160] the easy things easy with that. [02:00.160 --> 02:05.480] So the motivation of Fusion is we see that languages like Java get more and more things [02:05.480 --> 02:06.480] packed in there. [02:06.480 --> 02:12.680] We already have classes, methods, interfaces, constructors, traits in other languages, records, [02:12.680 --> 02:14.680] structs, packages and so on. [02:14.680 --> 02:22.480] In Fusion, all of these map to one single concept, which is the concept of a Fusion feature. [02:22.480 --> 02:26.920] Then I see today's compilers are much more powerful, so actually to distinguish whether [02:26.920 --> 02:32.920] some feature is used like a method or like a class or like a constructor is something [02:32.920 --> 02:38.600] that the compiler decides, then it is not needed that the developer decides that. [02:38.600 --> 02:43.960] And we see that more and more systems are becoming safety critical, so we need to ensure [02:43.960 --> 02:45.600] correctness. [02:45.600 --> 02:49.720] And I see that tools have to play in a very important role in ensuring this correctness [02:49.720 --> 02:53.480] by static analysis. [02:53.480 --> 03:01.880] Fusion is available on GitHub, there is a website flung.dev that gives an introduction into [03:01.880 --> 03:07.440] the language with lots of examples, lots of design documents, lots of ideas collections. [03:07.440 --> 03:08.440] Please go through that. [03:08.440 --> 03:16.200] I can't give a language introduction here, but yeah, you'll find more there. [03:16.200 --> 03:20.600] Fusion is backed by a small company, Tokiwa, with currently four employees. [03:20.600 --> 03:26.120] One of them is sitting here with us in the group, Michael. [03:26.120 --> 03:27.680] Now coming actually to this talk. [03:27.680 --> 03:33.680] So I will start with a very quick introduction into what the Fusion language looks like from [03:33.680 --> 03:41.920] a Java perspective, then talk a bit about side effects and their dangers, then propose [03:41.920 --> 03:48.440] algebraic effects as a solution to manage side effects and give lots of code examples [03:48.440 --> 03:53.040] how you could do these things in Fusion. [03:53.040 --> 04:01.840] So here a small example in Fusion, I will give a Java equivalent on the right side and [04:01.840 --> 04:05.520] the Fusion code on the left side that you can quickly understand what it's about. [04:05.520 --> 04:10.360] So I said Fusion maps Java features to Fusion features. [04:10.360 --> 04:16.880] So in Java, if you have a package in Fusion, it's just a Fusion feature, in this case Demo. [04:16.880 --> 04:23.520] If you have a class in Java, it is also a Fusion feature that is nested in another Fusion [04:23.520 --> 04:27.160] feature. [04:27.160 --> 04:35.080] If you have a method in Java, it is again a Fusion feature that is nested in this case [04:35.080 --> 04:37.840] in the Hello surrounding feature. [04:37.840 --> 04:42.760] In this case, what makes this feature different is that it's a function that returns a result [04:42.760 --> 04:48.920] which you can see from the result type here, which is unit. [04:48.920 --> 04:54.640] Unit type in Fusion is pretty much what the void type is in Java, but with the exception [04:54.640 --> 05:00.000] that it is a real type, so you can declare variables of type unit, you can return this [05:00.000 --> 05:01.000] as a value. [05:01.000 --> 05:07.920] It is not a very interesting value, but you can have this as a full-fetched type with [05:07.920 --> 05:10.480] only one single value. [05:10.480 --> 05:17.400] In contrast to that, void is also a type in Fusion, but that gets interesting again because [05:17.400 --> 05:19.920] void is a type that has no values. [05:19.920 --> 05:25.320] So basically the result type of something like system exit in Fusion would be void, [05:25.320 --> 05:30.280] which means it will never return. [05:30.280 --> 05:36.680] Then printing something is easy, there is a standard library function say that, in this [05:36.680 --> 05:39.560] case, prints hello world. [05:39.560 --> 05:45.880] Fusion uses a lot of type inferencing, so the result type unit here actually can be [05:45.880 --> 05:50.920] inferred because that's also the result type of say, so we don't need to explicitly note [05:50.920 --> 05:54.120] this. [05:54.120 --> 05:59.600] Then, I'll go back here, very similar to Java. [05:59.600 --> 06:02.680] If you have code like that, you don't have anything to run yet. [06:02.680 --> 06:05.080] You need in Java, you need some main function. [06:05.080 --> 06:11.200] In Fusion, there is one feature which is called the universe which surrounds everything and [06:11.200 --> 06:19.200] code put in the universe like here gets executed if you run your application. [06:19.200 --> 06:27.440] You can pass arguments to features and arguments are fields within those features and fields [06:27.440 --> 06:33.280] are also features, so they come in the same class, but features that are pre-calculated [06:33.280 --> 06:36.960] that hold a value. [06:36.960 --> 06:43.440] Fusion has inheritance, so you can define a feature hello to that inherits from hello. [06:43.440 --> 06:50.240] You can create an instance of that and call features on that. [06:50.240 --> 06:58.760] That much to a quick introduction into the language syntax and how it works. [06:58.760 --> 07:04.960] There's a number of things that Fusion does not have and mostly because these are things [07:04.960 --> 07:10.600] that are considered to a certain extent harmful in a safe to critical environment. [07:10.600 --> 07:16.400] There's no dynamic loading, there's nothing like macros, no reflection, no pointer arithmetic. [07:16.400 --> 07:19.040] Many of these things also Java doesn't have. [07:19.040 --> 07:24.840] There is no uncontrolled mutual abilities, so you cannot easily change variables. [07:24.840 --> 07:29.400] There's no direct support for exceptions in the language. [07:29.400 --> 07:32.360] The reason for this is we must know what the code does. [07:32.360 --> 07:38.600] We want to do static analysis of the code to ensure safety and also to a certain extent [07:38.600 --> 07:46.760] to allow better optimizations to increase the performance. [07:46.760 --> 07:49.600] Bitmare more on side effects and security. [07:49.600 --> 07:57.840] We learned a lot about security today already in earlier talks, but mostly addressing security [07:57.840 --> 08:04.640] aspects of the software development process and managing of security issues. [08:04.640 --> 08:10.880] I now come from the language side and say what we could do from the programming language [08:10.880 --> 08:16.560] to improve the security. [08:16.560 --> 08:21.280] If you look back at recent securities a lot, we learned about lock for J today, but there [08:21.280 --> 08:25.960] are similar things with Spring Shell, even the Rust community has similar issues. [08:25.960 --> 08:36.680] What these issues have in common is that library code that is used has unexpected side effects. [08:36.680 --> 08:42.080] You use a logging library, you don't expect this to go to the Internet and make an arbitrary [08:42.080 --> 08:47.000] connection and download code from somewhere else in the world. [08:47.000 --> 08:50.120] That is the common problem. [08:50.120 --> 08:58.600] One way that is used by many new upcoming language to control side effects is to use [08:58.600 --> 09:01.800] algebraic effects. [09:01.800 --> 09:06.440] Let me quickly explain to you what algebraic effect is. [09:06.440 --> 09:14.520] An algebraic effect is basically a set of non-functional operations that code might [09:14.520 --> 09:15.520] perform. [09:15.520 --> 09:21.880] These are operations that do not have an effect on the actual calculation on the return value [09:21.880 --> 09:24.200] of a function. [09:24.200 --> 09:36.280] Java already has one kind of algebraic effect built into the language, which is throws for [09:36.280 --> 09:39.080] methods that throw exceptions. [09:39.080 --> 09:45.520] But algebraic effect is more a broader concept. [09:45.520 --> 09:49.160] This is just one example that Java supports. [09:49.160 --> 09:56.520] Any operation in an algebraic effect can either resume or abort. [09:56.520 --> 10:03.920] So typically, if an algebraic effect is reading some data from some external input, it would [10:03.920 --> 10:11.720] return the read data and resume operation with the value that was read, while an operation [10:11.720 --> 10:17.240] that would be something like throw an exception would perform an abort, so it will not return [10:17.240 --> 10:24.200] but jump back to the corresponding handler. [10:24.200 --> 10:34.160] Side effects can be implemented by different implementations of the effect handlers. [10:34.160 --> 10:41.760] So there is no strict fixed wiring from the operations to a particular implementation. [10:41.760 --> 10:53.280] And very similar to exception handlers, effects may be nested. [10:53.280 --> 10:58.880] There's a kind of contrary view to two words algebraic effects. [10:58.880 --> 11:01.440] You can see algebraic effects. [11:01.440 --> 11:07.400] What I've presented so far is as the effects that the code might have, but you could also [11:07.400 --> 11:12.280] see them as capabilities that the code might require. [11:12.280 --> 11:26.960] Martin Odelski is starting a big research project in that area. [11:26.960 --> 11:55.960] What I do is I define my exception, which is our exception implementation, as a feature [11:55.960 --> 12:01.040] inheriting from the base library feature simple effect, which is just a basic standard [12:01.040 --> 12:08.960] effect, and our implementation of throw is just abort. [12:08.960 --> 12:14.600] So the simplest way to stop an operation. [12:14.600 --> 12:22.760] And now we define one feature that throws an exception, and what we do here is we call [12:22.760 --> 12:30.880] the operations throw, but we need to have an instance of the algebraic effect. [12:30.880 --> 12:36.160] And the syntax we use in Fusion for that is we use the type of the effect, which is my [12:36.160 --> 12:39.080] exception, from the environment. [12:39.080 --> 12:46.880] So.n means taking the innermost instance of that effect in the current environment and [12:46.880 --> 12:53.200] calling that operation on it. [12:53.200 --> 13:00.960] When we do that, we should declare in the signature of the function that this function [13:00.960 --> 13:04.920] requires the effect my exception. [13:04.920 --> 13:08.600] So this is very similar to a throws clause in Java. [13:08.600 --> 13:13.000] If I throw an unchecked exception, I need to declare that. [13:13.000 --> 13:20.320] Here if I require a certain effect, I declare this with the exclamation mark. [13:20.320 --> 13:26.000] Now I add some prints just to show what this code actually does, and I want to call this [13:26.000 --> 13:29.840] function, this feature F now, to call it. [13:29.840 --> 13:33.520] I have to first install an instance of the effect. [13:33.520 --> 13:40.280] So I create an instance of the my exception effect here called use on is, which is a standard [13:40.280 --> 13:47.360] effect function that takes a lambda expression, which then calls the code that is executed [13:47.360 --> 13:51.520] while this effect is installed. [13:51.520 --> 13:57.960] So adding some more prints that you see what is happening, and if I now run this code here, [13:57.960 --> 14:05.840] you see that it prints, the exception is installed, it prints the before throw, throw directly [14:05.840 --> 14:10.840] jumps, very similar like an exception, out of the use here, and we continue with the [14:10.840 --> 14:11.840] we are done. [14:11.840 --> 14:18.320] So the code after an operation that aborts here will not be executed at all, very similar [14:18.320 --> 14:21.760] to exceptions. [14:21.760 --> 14:24.440] Yeah, now let me talk a bit about mutation. [14:24.440 --> 14:31.960] I told you that fusion doesn't allow direct mutation of fields, so fields are immutable, [14:31.960 --> 14:38.800] which means if we do have code like that, we declare a field x, assign 1, 2, 3 to it, [14:38.800 --> 14:46.160] print it, and then assign 2 times x to another field x. [14:46.160 --> 14:55.800] We see the expected behavior, but if we create a feature that prints this field x, and try [14:55.800 --> 15:02.800] to compile this, or try to run this, we actually get an error, because the problem is this [15:02.800 --> 15:09.800] x here is not clear which one is referenced here, because we have two different variables [15:09.800 --> 15:15.440] here, there's two axes here, the first and the second, and they are only visible for [15:15.440 --> 15:18.280] the code following that. [15:18.280 --> 15:25.400] So we get an error message that there are two different axes, and that source code position [15:25.400 --> 15:29.000] here doesn't know which one to choose. [15:29.000 --> 15:39.880] So really every assignment creates a new field, and these fields are immutable. [15:39.880 --> 15:46.440] To make them mutable, to get actually the desired effect that we can print x here, we [15:46.440 --> 15:53.400] would have to create a mutable integer value, which is with the base library function mute, [15:53.400 --> 16:00.000] creates a mutable instance, assign this to the variable x, and now if we want to update [16:00.000 --> 16:08.720] this, we don't create a new field, which would be the colon equals operator, but we have [16:08.720 --> 16:13.480] an error operator which updates the value with a new value. [16:13.480 --> 16:22.560] If we run this now, behaves first like the code before, but this time the show x function [16:22.560 --> 16:33.520] can actually access this single variable, because now we have only one field left. [16:33.520 --> 16:39.920] We can now analyze this code for the effects that this code requires, and if we do that, [16:39.920 --> 16:45.680] we see there's two effects, there's IO out, this performs output, and there's the mutate [16:45.680 --> 16:54.800] effect because we have an update of a mutable field in our code. [16:54.800 --> 17:03.600] Now not all variables, very few variables actually usually need to be mutable. [17:03.600 --> 17:14.080] Here's an example of a small loop with an index variable counting from 0 to 9, and printing [17:14.080 --> 17:20.920] them, if we analyze this code for effects, we see that this only depends on the IO out [17:20.920 --> 17:21.920] effect. [17:21.920 --> 17:28.840] The reason is that every loop iteration creates a new instance of that variable, so we don't [17:28.840 --> 17:36.280] update the i variable here, but we have one independent instance for every iteration of [17:36.280 --> 17:38.760] the loop. [17:38.760 --> 17:47.760] So no variable is mutated, a new instance is created for every iteration. [17:47.760 --> 17:55.880] I want to talk a bit about error handling now, and show how the function can produce [17:55.880 --> 18:01.160] an error, and show them three different ways of how error handling could be done. [18:01.160 --> 18:11.240] The function I use is just to divide, that divides two integers, and I call this in a [18:11.240 --> 18:18.000] show div function that calls divide and prints the result, and then I call this with three [18:18.000 --> 18:28.880] different value pairs, and if I call this, I get, not very surprising, I get an error, [18:28.880 --> 18:40.640] there is a division by zero, the precondition of the division is not fulfilled. [18:40.640 --> 18:46.040] So that's the standard error handling in fusion, but it's not very nice because you [18:46.040 --> 18:48.800] have the whole application for failing. [18:48.800 --> 18:58.200] If you want to now somehow treat that error, what we could do is return an outcome, which [18:58.200 --> 19:04.480] is similar to Rust's result, which is basically a choice type between an error and an actually [19:04.480 --> 19:12.600] 32-bit integer, and check the case, if B is zero, we return an error, otherwise we return [19:12.600 --> 19:17.680] the result of the division, and if we run this, now the application runs through, it [19:17.680 --> 19:27.600] doesn't terminate, and in the middle case, we print the outcome, which is an error here. [19:27.600 --> 19:37.240] But if we want to now actually, after calling the divide, want to know was this divide successful [19:37.240 --> 19:44.200] or not, we would need to check the cases, so we need to distinguish whether we actually [19:44.200 --> 19:53.120] got a value, or we got an error, we can do this with a match over the different choices. [19:53.120 --> 20:01.600] Now an alternative would be to use the standard library try effect, which is kind of the default [20:01.600 --> 20:11.400] exception based on algebraic effects in fusion, and to do that, instead of returning an outcome, [20:11.400 --> 20:18.800] this would be just a function returning a 32-bit integer, but requiring the try effect [20:18.800 --> 20:29.000] to be installed, and now instead of causing an error, we would raise the error of the [20:29.000 --> 20:34.360] try instance in the current environment, so we don't need the else anymore because the [20:34.360 --> 20:41.920] raise would abort and would return immediately, so we could just continue with the code there. [20:41.920 --> 20:51.040] And when we call the divide now, we have to call it with an instance of the try effect [20:51.040 --> 20:58.120] being installed, so just like before, this can be done through a base library function. [20:58.120 --> 21:07.200] Try that installs an instance and calls the lambda, which is provided as a parameter, [21:07.200 --> 21:16.200] and this can then be matched very similarly to the outcome, but the big difference is [21:16.200 --> 21:21.480] that now the code in between, in between the position where the error is, and where we [21:21.480 --> 21:27.280] have this call, does not need to pass along these outcomes all the way, I'll come to an [21:27.280 --> 21:39.920] end very soon, but we can directly work with the i32s and the try would jump out directly, [21:39.920 --> 21:43.280] so we would see this outcome. [21:43.280 --> 21:52.280] So the penultimate slide, the current slide, the status of fusion, it's still very much [21:52.280 --> 21:57.000] in development, the language is getting a bit more stable recently, but there's still [21:57.000 --> 22:01.000] a lot of work, mostly also in the base library. [22:01.000 --> 22:06.160] The current implementation has two backends, one running on a JVM, and there's also a C [22:06.160 --> 22:13.360] code backend, and there's basic analysis tools available, as I've shown you, the effects [22:13.360 --> 22:17.400] analysis. [22:17.400 --> 22:25.520] Java maps actually very well to fusion, there's a tool that allows calling all of Java APIs, [22:25.520 --> 22:31.520] creating Java APIs from a fusion APIs from a Java module that we can call into Java, [22:31.520 --> 22:36.680] what doesn't work yet well is calling back from Java into fusion, but there's at least [22:36.680 --> 22:40.300] in one way, it's one-to-one mapping. [22:40.300 --> 22:48.280] We have effects to encapsulate non-functional aspects, and I ask everyone please have a [22:48.280 --> 22:53.920] look, we're happy for any feedback. [22:53.920 --> 23:03.920] Thank you for staying so long, I think time is over. [23:03.920 --> 23:23.280] The match is still needed because this try here installs the effect, and an effect in [23:23.280 --> 23:32.240] the case of an abort has to provide some way to join the value that is returned in the [23:32.240 --> 23:37.880] non-abort case with the value that is returned in the abort case, and for the try effect [23:37.880 --> 23:43.680] this join is just made by producing a value of type outcome, which is the choice between [23:43.680 --> 23:50.320] error and the value, but there could be other effects that would just replace it by a default [23:50.320 --> 23:57.320] value in that case, so it depends on the effect, but here it's definitely still needed, yeah. [23:57.320 --> 23:59.320] Do we have time, yeah? [23:59.320 --> 24:05.320] Yeah, I saw that at some point you showed that there was an IO effect, and I also saw [24:05.320 --> 24:10.320] a lot of code that uses the same function, which I presume uses that effect, but can [24:10.320 --> 24:13.320] you see the effect using any of the examples? [24:13.320 --> 24:20.320] Okay, yes, you took very good care, thank you. [24:20.320 --> 24:25.320] Yeah, it is not decided yet where the compiler should be strict and require this annotation. [24:25.320 --> 24:33.320] The current idea is that for basic code we should not require this annotation, but for [24:33.320 --> 24:38.320] a public library function we definitely want to know what are the effects. [24:38.320 --> 24:45.320] So, I don't want to enforce this for everything or for all the intermediate values, and there's [24:45.320 --> 24:51.320] also some cases where only a static analysis of a whole application can actually determine [24:51.320 --> 24:57.320] what the effects are, so static analysis plays a very important role there. [24:57.320 --> 25:04.320] I don't want to enforce too much typing basically for these effects here. [25:04.320 --> 25:10.320] Another one, there is, like John for example has this distinction between runtime exceptions [25:10.320 --> 25:16.320] and checked exceptions, and there are just these kind of exceptions that can have pretty [25:16.320 --> 25:22.320] much any code, like out of memory exception or static load, and I wonder how do you handle [25:22.320 --> 25:24.320] these kind of cases? [25:24.320 --> 25:28.320] Oops, they're shutting us down here. [25:28.320 --> 25:29.320] Okay. [25:29.320 --> 25:31.320] It's a small hint. [25:31.320 --> 25:38.320] I actually, it's not nothing of that is done yet, but I think I would like to get one step [25:38.320 --> 25:42.320] further and make it user configurable. [25:42.320 --> 25:49.320] What are the effects that you want to have considered acceptable in your environment? [25:49.320 --> 25:55.320] Like you want to have maybe add some debugging print or some logging in somewhere nested [25:55.320 --> 26:02.320] in some internal function that shouldn't have forced you to add effects all over through [26:02.320 --> 26:03.320] the code. [26:03.320 --> 26:08.320] So we must have some set, some way to define for the debugging build. [26:08.320 --> 26:14.320] These are the effects that are in there, and please don't complain about that. [26:14.320 --> 26:19.320] But we have to still see how we actually will do that. [26:19.320 --> 26:21.320] Thank you so much. [26:21.320 --> 26:24.320] Thank you for saying so long. [26:24.320 --> 26:25.320] Thank you. [26:25.320 --> 26:28.320] Thank you very much for attending the freegear room. [26:28.320 --> 26:31.320] This will be a room next year again. [26:31.320 --> 26:36.320] Hopefully we'll have two days and we'll have more time for sessions and hopefully many [26:36.320 --> 26:38.320] of you will submit proposals. [26:38.320 --> 26:42.320] You will all be very welcome to present in the freegear room next year. [26:42.320 --> 26:54.320] Thank you very much for coming.