 All right. Hi, I'm Klacke. For the last year, I've been working with mainly Stuart back there and with Dennis. Is Dennis here? There's Dennis. And together we've been creating Fractalide. And Fractalide is a flow-based programming framework in Rust and in Rackets. Last year we've been focusing on the racket part. Flow-based programming. What's that? So if you take your usual control flow diagram that explains how your program counter goes through your imperative program. When you do data flow programming, you describe where the data goes instead. And that enables you to do all kinds of interesting things easier than if you have to describe exactly what's happening in the detail in the system. So there's lots of different data flow programming methods and flow-based programming is the one characterized by these things, mainly the bounded buffer and the asynchronous nodes where you have parallel processing going on. This thing here can process things and further on, the data travels on and more data can come in. So the data travels in waves through the graph. If you want to know more about flow-based programming, this is the canonical source. So why do we want flow-based programming? The holy grail of software is software reuse. Ever since the first lines of code were written, we were figuring out how can we not write this line of code again, even though it was a very good line of code. So we started by making programming languages and programs more modular. For example, the modular programming language, we'll start back in the 60s. We went from there to figuring out object orientation with Simula and later Smalltalk. And then we started looking in the large. We have this code here. How should that interact with another piece of code? And we started talking about service-oriented programming or architecture. Defining interfaces between things and having them talk to each other. This led to a situation for historical reasons that a lot of people didn't like. So they called it something else because a lot of connotations got attached in certain standards and so on. So microservices, it's really the same thing. You have something running here. You have something running here. They need to talk to each other somehow. One specific way to talk to each other. I don't know if maybe Chris mentioned. He's really into the actor's paradigm of putting things together. So that's also a system where you have asynchronous agents that send messages to each other. So how is that different from flow-based programming? These are the things we want when we have our ways of putting things together. If someone knows too much about how someone else works, then when you change something you need to change in too many places. So you want to make sure you have as little knowledge as possible and in as few places as possible. You need a way to know how these different parts of your program talk to each other. You need to make sure, and this fits together with the previous two points, to make sure that if I change this thing, it doesn't affect this other thing unless I want it to. And one aspect of this is if this module is going to communicate with this module, it better not be hard-coded to know about this module. It should know about some kind of interface and we should somehow tell it that, okay, here's the thing you're talking to now so we can switch things out without having to change that code. So here's an example of flow-based programming in the language you're possibly familiar with. You have one node that sends out some kind of message. In flow-based programming we call it an information package, but I'm going to use message because most people will find it easier to intuitively get what it's about. So we have this node that generates a message. We connect it to another node that does some processing of this message, sends out a new message. We touch another node that further processes this, does things, maybe selects things, transforms things, and sends it out. And then if we're doing this in a shell environment, we have an implicit display agent at the end that shows you what the end result is. So in flow-based programming, that curl thing there, that would be a component, and the component doesn't take a parameter. But when we do this kind of simple pipeline, we just have one standard in and we have the standard out. When you do flow-based programming, you have several inputs, if you want to, and several outputs. And one magical input is the option input, opt. If you want parameters, you send them there. So we would send the URL to our curl module instantiated into a get URL node. And we would send this as the first input that starts the whole chain. You have a graph that just sits there until you send some message that makes it do stuff. So the first part of this graph is the curl agent. It receives a message, hey, here's a URL. It does something. It goes to the network, gets this data, sends it to its out. In the meantime, we have sent the other messages to configure the other nodes. So we have sent to tidy, you should make this into XML, to its opt input. And we have sent this selection expression to XML Starlet, that you should do this with the data when it comes. So it gets that on its opt input and then at some point on the in input, it gets this XML. And then it does its thing and sends it out and we get the result. So this is a bash pipeline syntax. It's quite neat to describe just a line of things. But what if you want to have, like this thing produces something, I want to split it up into two parts, process something over here, process something over there, put them back together. You can do it, but it's very tricky and you cannot just stay within bash. You need to use some other tools to set up your pipelines and stuff. So flow-based programming is really a certain of these historical methods put together in one package. To implement it, you can use coroutines or threads. And you have kind of actors, but they're limited in what they can send. If you have the actors paradigm, you usually find out who you should send to next by getting a message. We do more information hiding in flow-based programming. We don't tell you who you're talking to. You just send it to your out or to your, here's where I send pictures or whatever outputs you have. And the scheduler takes care of that and gets it to the right place. So that enables more combinations. You have less information and you have less risk of some unintended consequence somewhere. Multiple inboxes and multiple outboxes. And the pipes that we call them in bash are called edges. Messages or information packages. You can type things if you want to. We do it on the rust side. We don't do it on the racket side. Or we can do it on the racket side just by saying putting a contract on the input of that agent. But we don't check it during graph compilation time. But if you write fractalized network in rust, when you compile this thing, it will already verify that all the types fit together. And here's something maybe it's possible to do if you set up your anonymous pipelines or named pipelines just right, but it's very tricky to do. But you can have feedback loops. Imagine, for example, like a toy example is the Fibonacci sequence. It takes its own input to the next iteration. You can do that very easily as a flow-based programming network. You consume values here. The initial value is one and one. And then it produces the two. It goes around, oh, I got a two. And then it adds that to the latest thing. You've got your Fibonacci sequence coming out the other end. Here's the terminology in agent. You take a module. You instantiate it. You've got an agent. Ports, that's the inputs and outputs. Edges, that's the bounded buffer. And you can, if you are using Fractalite, I think there are 30 objects long. If you use Morrison's frameworks and the FBP syntax, you find how long they are. And you can make them infinitely long if you have to by putting them into a file and putting them back in. But the bounded buffer thing is really important. You can have a typical example is you have an agent that calls itself concatenate. It listens on the first port until the data ends. And then it starts listening to the second port. If the thing that produces data for the second port keeps running while the concatenation is not listening, you have to store all that data somewhere. So what happens is the buffer is full. And the sending agent suspends until we start reading, then it keeps running again. So this keeps resource consumption low. And it stops you from getting any kind of... You cannot get live locks in a bounded network. You can get deadlocks. It's very easy to get deadlocks. But it's also quite easy to see them and avoid them. The graph, that's the set of agents and edges. And a node. That can be either an agent or it can be a subgraph. So you put graphs into graphs. So that's why we have the expression node. Within a graph, you can just talk about your nodes. And maybe this one is an agent. Maybe this one is a subgraph. Here's how you could make an agent in Fractalite. So we define that we want certain inputs. Often in these simple examples, we just have one input. And one output. And then we have a simple function. You can choose not to block on a port. So maybe you send some option to this. Maybe you didn't. And if you did, we display that thing. Otherwise, we display the message that came in. So this one is a probe that you can insert into your graph. You have defined your big graph. Something is going wrong. You want to know what happens here. And because everything is decoupled and everything can be rearranged the way you want. You just take the data that was on its way from component A to component B. You send it to the display instead. And then send it on to component B. And then you can get your printf debugging right there. Here's a trickier thing. We put a bunch of things together. And in Fractalite, we can also achieve GUIs using these networks. So we have a GUI message there. That's a label. Just some text that is shown in a user interface. It says password, because that's the initial package that we send to it. Then we have a Cardano wallet password. That's a subgraph. We don't have to know anything more about that right now. It has its inputs and its outputs. So we can treat it like a black box. It's some kind of component that allows us to change the password. From Cardano wallet password, we use the output thing. That's an internal thing in how we use the graphic components to put them together. You take a graphic component. You connect its outputs to some kind of graphical parent, the container. And you can number them so that they are put in the order you expect. The password thing has an output that is the password. So you change your password. When you have changed it, you have clicked Change Password. That goes out as a message somewhere. Maybe you send it to some back end that actually changes the password. Here, we're sending it to a displayer first. So do I have a mouse pointer? I have a mouse pointer somewhere. There it is. Oh, that is too fast to see. That's going to be too difficult. The live demo here is I click Change Password. The image changes. I get a prompt and a confirmation. And I fill in the password two times. I click Change. And then I flip over to our console to see that this displayer has actually put out my password as a debug output. Here's something trickier. We take now this password thing. Oh, sorry. This is the same. We take the change password thing and we put it into a bigger context. Now we treat this whole thing that we saw here. This is now a black box in a larger context. So if I click Change Password here, this part of the application shows that box that we saw in the previous example. And I can change password. I click OK. It goes back to looking like this. That's it. I think there's a lot of stuff to take in. But I think maybe you have some general idea of you can actually do things with these graphs. And they allow you to put together different components without changing the components themselves. And you can do it in Racket. And I haven't used Racket before this year. What has been a really great experience. The Racket user's mailing list is an excellent place to be. You see people asking total newbie questions. And the professor that was one of the people that created this thing comes back with an answer. Oh, I changed this. I wrote a syntax that changes the language a bit to do what you like. Maybe that's what you need. Before we started working on this wallet, I did some back-end work. I created Racket to Nix, which takes Racket packages and puts them, makes Nix derivations of them. And we told Racket users this. Hey, I'm working on this thing. And I immediately got a PR from one of the authors of Racket. Like, hey, I looked at your code and I reformatted it the way that we like to see code in Racket. Because I was writing in a scheme as I learned it from the wizard book 10 or 15 years earlier. So that was really cool. You start playing with the language. And then one of the really important guys comes in and, hey, I fixed your thing. Wow. That was really cool. Any questions? More about flow-based programming or Racket? Sorry? When can I use this in real life? You can use this in real life. You'll have to tolerate that the syntax looks like this because we've been focusing on getting things working and having actual front-end examples. And the wallet I showed here, this is a mock example from a couple of months ago. The code looks a bit different now. And we have an actual back-end attached to dust things. But, yeah, you can totally use this. Talk to us to find out how. The documentation is not exactly where the code is. Documentation is here. So the last time we talked about this, we said, oh, more documentation is coming. Check back in six months. And then I had to correct myself. Like, okay, check back in six months and please start writing documentation for us. But, like you saw, we have running code and it can do stuff. Yes. And that's why I just arrived here last minute because I was trying to get it to work for the demo. We have a graph editor. But, yeah, you need to set up everything just right so that everything finds its classes and stuff. So I thought I'd had it working. But then, oh, now when I load this, it tries to load some widgets and everything crashed. And then I decided to skip that part. Well, you can see, you write in the text, the Lispy language that you saw. And then when you load it in the graph editor, it uses GraphVis.to to lay out these things and you get your boxes and arrows and you can edit things there as well. The graph, yeah? So I put this in a module file called something and then that's my component. And here when I say node, VP, GUI, vertical, Panit, that's such an agent or a graph. So I give this code the name displayer. I call it displayer.racket. I put it in the right directory. And what happens here, three lines from the end, it says node, display password, displayer. And displayer there, that loads the code on the previous page. When we put together the graphs, we use strings to name things because we just do. No, you name the node and you can have several nodes that use the same component and they are independent of each other. So that's why I define the node. I say I have a node that is named an arbitrary name and it uses the component which has a name that needs to match by name and using rackets way of looking. It does a dynamic require on it. Yeah, the names of the components are symbols and the names of the agents, the instances of the components are strings. Yeah, we got three more minutes. In this presentation, nothing. But yeah, I can mention some more things about rackets. One issue with rackets has been performance, sadly. When we load the whole wallet graph, it takes several seconds for it to come up. That's because the scheduler that runs the graph is also a graph. So there's a bootstrap process going on there. And when it talks to itself while loading these things, maybe half the time is spent actually loading and telling racket dynamic require this and that and half the time is sending messages using the async ports in rackets. I profiled it and the async ports really take a lot of time in rackets. So we don't have like right now the racket part and the rust part are pretty much separate things. We have a rust fractal light and we have a racket fractal light. But once we put them together, if some part of your racket thing has performance issues, you can just reimplement that in rust and then you insert that into the graph. And the rest of the graph won't know. This is just a subgraph. It's a black box. Thank you.