 This is an experience report and this is meant mainly for tinkerers. If you are the kind of person who tinkerers with coding off your day job, then probably you will enjoy this talk. Okay, question time. Can I get a hands up vote of your favorite programming language? Top 1. Okamal. Okamal, fine. Haskell. Haskell. Erlang. Oh excellent. Okay. Beg your pardon. Closure. Closure. Okay. Undecided. Undecided. Not bad. See, excellent. Okay. So I see Haskell dominating the room and but there are acolytes for Erlang and other languages also. Now, follow up question. Can you think of a programming language which you would say is diametrically more or less opposed to the language that you just chose? Haskell. Haskellers. See. Okay. Okamal guys. Java. Okay. List. Go ahead. I have to say. Yeah, go ahead. Okay. Now, so look, so the way I see it, it seems to be the diametric opposite of functional seems to be imperative. That seems to be the immediate answer. I am an Erlang evangelist of sorts because I tend to pester my friends with the tales about how Erlang makes this easy and that easy and that fun and so on. So one of my friends got tired of it and said, you know, before you talk about elegance, why don't you actually look at fourth and then let's talk about elegance and simplicity and how things are easy. Consequently, I was curious. I mean, this was an old timer who was like, you know, Haskell, everything from C to Haskell and everything in between. And when he told me about fourth, I started looking it up. What is this fourth language? Turns out that fourth is pretty old. It predates C and it is probably owned. It was born around the same time as Fortran and it has been in more or less continuous development in various forms and yet nobody has heard of this language. So I figured that I'll go with fourth and see what it has to teach me about elegance and simplicity. And what is it that my Erlang friends or my Erlang listeners can gain by me learning fourth? So these are my answers. Erlang is probably my favorite language. And fourth, I consider diametrically opposite for various reasons. Anybody here have you used fourth? You have used fourth? Okay. Excellent. A fourth is an imperative language, but I don't think that's what it makes it diametrically opposite to Erlang. Fourth is a stack oriented language, which is a completely unfamiliar paradigm. I mean, you might have seen the odd bits of imperative code in your functional languages somewhere. If you're using Lisp, you can write imperative code. If you're using Erlang, you can write a bit of imperative code. Even if you're using Haskell, you can write some imperative code or probably you would be short for that, but still you can still do that. But fourth is, I mean, it's imperative, but its imperativeness is the least of its characteristics. And the most prominent characteristic is that it is a stack oriented language. Erlang is compiled. It runs on a VM. Fourth is threaded and interpreted. Compiled. I guess everyone here understands what compiled means. VM. Beam. JVM. All kinds of VMs. Threaded. Yes. What is threaded? Gases. What is threaded? Because threaded is what I found most fascinating about fourth. It's not threaded in the multi-thread sense of the word. It's not threaded in the green threads versus P threads versus system threads sense of the word. It's completely different. So here it goes. Thread is a way in which the fourth language behaves or is constructed under this hood. So there is threaded code and all a fourth it can be considered as one giant thread. Start at the beginning, all the way to the end and your entire language is right there. Some caveats, but more or less it's a one giant thread. It can be thought of as a humongous linked list. How many programming languages do you know which have this internal structure? So you can say that it is a thread and that elements in a linked list are threaded together. Now adding and removing from this thread is where the compilation comes in. It's not exactly compilation as we are used to it, but in fourth when you are compiling something new, you are essentially adding or removing or overwriting into this thread as we'll see in a second. Interpret it. Now you have a thread of internal forms. We'll come to what those are in a second. But we have a thread of things which can be executed. You can execute them consecutively or in a particular order. You start the thread at one point and follow it down until you reach your desired objective and then you execute it. You don't need secondary analysis in the sense that when you're trying to execute it, all you need to do is search down the list, find what you're looking for and then execute it. Am I making sense so far? You combine these two characteristics, a linked list of fully analyzed forms and a mechanism to execute it and a mechanism for humans to interact with it and you get what is called a threaded interpretive language or till. Internal form, we'll come to it in a second. Internal form is essentially how does fourth give you a piece of thing which can do some action? How does it give you a executable? Internal form can be loosely compared to your compiled binary code or machine code which actually does something. So fourth is a threaded interpretive language. Is anyone here familiar with ink on paper art? It's called ink wash. This is one such. This is by a Chinese master called Chi Beishi and that's shrimps. All it uses is black ink of varying degrees of opacity. That's it. But using that single technique, you're able to produce highly complex mesmerizing forms and that I think is analogous to what fourth is capable of doing. Fourth, while the internals are simple, is able to come up with complex structures using those relatively simple techniques. In that it is similar to our language. In how the language itself is minimal, easy to learn, fourth, so is fourth. So fourth has simplicity. It is easily extensible. We'll see how. And it uses stacks and reverse polish notation. Anybody here familiar with reverse polish notation? Okay, excellent. So for somebody coming from infix or even post fix, reverse polish notation, it takes some time getting used to. I've worked in scheme. I've worked through SICP. So I'm familiar with scheme and that is relatively easy if you're coming from infix, moving to something like scheme or Lisp is relatively straightforward. But RPN, that takes getting used to. So in fourth, the whole language can be looked at perceived as a series of words. A word is what you would nominally call a function in other languages. Let's quickly look at some code. So how do you add four and three? By the way, a backcourt is how you initiate a comment. So four and three, the first line you are adding four and three that you type on the interpreter. So four, three and plus. What is happening here? When you type four, fourth detects that it is a number. There isn't much to do. It is pushed to the stack. If you type three, which is again a number, nothing must to do, push it to the stack. Then comes plus, which is actually a fourth word and it knows what to do. The code corresponding to the plus is located and executed. That code is programmed to remove the last two numbers or the top two numbers, top two items from the stack and attempt to add what happens to the output back to the stack. So at the end of this operation, you would have the sum of four and three on the stack. So far, so good. Next, you want to define a new function. I want to define a function which is called four more. The colon, keep note of the colon, that colon is used to define something new. I'm defining a new fourth word, that word is called four more. What does it do? It first pushes four to the stack, calls plus, semicolon and the function definition. Now how do you add four, three and four more? That's it. So for us who are used to saying function followed by arguments, this takes some time. At least it took me some time to get used to. You're not operating in terms of, you don't perceive the arguments being passed to the function as quickly and easily as you would in a normal functional language. Because the arguments are sometimes invisible to you, they're on the stack. Now let's see, 20 by four, I mean it's effectively dividing 20 by four. So 20 goes on the stack, four goes on the stack, division operator comes in and works. This is one example. Let's take something more complex. This is from an actual fourth book. And washer, how does a washer work? It's the top level code for invoking a washer. Think of your main function in C or a public static void main in Java or an entry point in some of the other functional languages. You have a word called washer, or a function called washer, which invokes wash and then spin and then rinse again and spin. Yeah, beg your pardon. What does it go on to the stack? Let's assume that you have no arguments. In that case, wash would kick in first, then spin, you mean you push something on the stack, then spin it, rinse it, spin it, wash it again. Not necessarily. This doesn't say anything about the stack position. It doesn't say what is expected to be on the stack and in which order. It goes from left to right only. You have wash, but you don't know what wash operates on because it doesn't tell you anything about that. We'll see how stack things are mentioned. I mean, what is the expectation of the stack? For example, if you look at the previous one, plus expects that the last two numbers on the last two items on the stack be numbers. Beg your pardon. You have a stack of arguments and you have a yes, yes, yes. Let's look at some built in stuff. We see on the right hand side inside parenthesis, that's how you specify the expected stack order. It's part of the documentation rather than the function signature. You don't have a function signature in the way you used to in other languages. For example, to know what doop does, it is effectively duplicating the top element of your stack. You have to either read the code or you have to look at the documentation. Now you can think back and see, I don't know what wash does. You have to look at the documentation to find out what exactly does it do. Let's look at doop. Essentially, it just duplicates the top element of your stack. If you have 10 on the stack, it makes it 10 and 10. The parenthesis notation is something of a convention which is used in the fourth world to indicate the stack before and after the operation. You will see two dashes separating a set of arguments. The two dashes indicate the operation being performed and the left and right hand side indicates the pre and post status of the stack. So let's take doop. Before the operation, you have one element on the stack or rather we are concerned with only one element on the stack operation. Then you have two elements on the stack, rot, which is rotate. You have three elements on the stack, the third element or the third from the top is rotated up front. Swap, you have two elements on the stack that position is reversed. All operations are executed from the top of the stack. So when I say A and B, B is at the top of the stack. A is one level below the top. And after swap, now A is at the top of the stack. Drop, you had an item on the stack and you have thrown it away. And two doop, which effectively is duplicating pairs. It expects two elements in the stack minimum and those two elements will be duplicated. So far so good, clear? Okay. Question time. Given this, you have four items on the stack. This is the expected order before and that's the expected order after. Given those operations that you've just seen, can you? Sure, sure, sure. It's fun, I mean, I enjoyed during, when I was learning 4th, I was doing some 4th exercises and it was a lot of fun doing those exercises. Do I have higher order words? Which is, I didn't understand. So you could rephrase, you don't put functions on the stack, no. First order functions, you mean, no. Unless you make your own version of 4th, the classic interpretations of 4th do not give you first order functions. It's not a, don't think, treat it like you would treat a functional language. Because you, initially I tried that and it just, yeah. No more ads. Okay, go on, let's move, okay? Here we go. So I've written a function again. It's not a function, function, it's a 4th word. Reverse top four. So you have a swap, a two-swap and a swap. You have two doop, imagine. But even if, no, no, two-swap is also easily defined. You just write your own two-swap and then, yeah, yeah. But I'm sorry, you can solve this in multiple ways. The point is that you can, this is how it works. So I've broken down the operations here. You have A, B, C, that's after the first swap. You see D and C have changed positions. Then you do a two-swap, they've changed positions again. And you finally do a swap and you're in place. But this is not the only way to solve it. I mean, with clever combinations, you can do a rot, rot, rot or whatever. I'm sure you can get it done. Or you can write your own three rot or four rot. Yeah, sure, sure. Okay, let's move on to something a little more fun. So you have A minus B divided by A plus B. Expectation, A and B will be on the stack in that order and you want the result. We'll move on to the code. So here we go. This is one solution. You have others. So you have two dip, then subtract, then rot, rot, then plus and divided by. I mean, this was like, I had a lot of fun doing these exercises. It is like solving a puzzle and it kind of gives you a frame of mind or a way to look at problems which you wouldn't get from any functional language. So that's the unique perspective of fourth. You're different. See, you don't really think in terms of the argument stack when you're writing a functional language. Yes, please. That's great fun. You probably missed the introduction. I read Java for profit and Erlang for fun. So there is Chuck Moore and a suite of companies that he has founded. They successfully pulled this off. Yes, the memory footprint is very low, very low. In fact, you had classic fourth books coming out in 78, which told people that you could do better than basic and Fortran in terms of memory consumption. So you could program on your toy device and still have a useful programming language. Okay, now, re-factoring, how many here have heard that word? Okay, so let's split it. It's re-factoring. Chuck Moore, the creator of fourth has been known to yell, factor, factor, factor your code. Hang on there. This is from the GNU fourth manual. So your short fourth words was some, one of those things, it's like C programmers writing themselves on writing a classic C code or LISP was writing very subtle things or Pearl guy's writing junk or, but the point is you're supposed to find the shortest possible way to reach a solution. And that was called factoring. The word comes from factoring in polynomials, where you break a polynomial down into its factors. Modern programming, when you say refactoring, you're reaching or you're talking about the legacy of fourth. Refactoring is nothing but factoring your program again without changing the way it behaves. It depends on who is doing the factoring. I mean, any polynomial can be broken down into. If I were writing it now, that would probably be the case. Yeah, but okay, this is another piece of art. I just liked it, so I'm putting it here. This is from one of the fourth textbooks. It talks about the divider operator. So that's a stack and that's your samurai divider. So he flings things into the air and then chops them up and then puts the result on the stack. I like the cartoon, so that's why it's here. That's how the return stack was. Okay. So you have a parameter stack and the implementations that I've seen, the operating system gives you a stack and that's the one which people usually use for storing parameters. I mean, you can write your own stack, no big deal, but convention seems to be that you use the always provided stack for storing parameters. You have several built-in stack operators. So when you see things like Doop and so on are implemented, not in fourth, but for performance reasons, they are implemented in assembly. Since the whole language is built on top of a core of assembly, certain built-ins are written in assembly for making it faster. There's a flip side which may or may not matter to some programmers is that the more your code is in assembly, the more difficult it is to port it from system one to system two. So if you have your mass rover going to Olympus, Mons or wherever and you want to port it there, you would have to undo and redo all those things because it may have a custom chip which Intel has made for you. Whereas if you're working on a standardized Intel or ARM or any of those mass market chips, this is less of a problem. So you trade speed for portability. Let's quickly look at a threaded interpreted language in three acts. This is a high-level overview of how a typical fourth or a threaded interpreted language will be implemented. So you have a dictionary. You have an interinterpret which I think is the crux of how fourth works. Hence, I'll focus mostly on the inner interpreter and you have an outer interpreter because a language which does not talk to the external world is not very useful and the outer interpreter is the part of the language which talks to the operator, the programmer and gets things done. So you have primaries and secondaries. What are primaries? Primaries are essentially what I just mentioned. Code written mostly or entirely in assembly. They're there. They're built in and they're ready to go. Secondaries are things which are built on top of primaries. Now these secondaries might be something you just compiled or maybe something which fourth already gives you. So we come to the first act, the dictionary. I'll speed up a bit because all our fun discussions have taken away some time, so here we go. So in fourth, the first distinction. You don't call them functions because they're not functions in the normal or familiar sense of the word, they're words. And where do you put words? You store words in the dictionary. That's your best place to go for words. So I have a definition here, double. Double is defined as dupe and then plus. Or I have quadruple which is two doubles. So you have a dictionary and this is one form of dictionary. I mean you can have clever dictionary layouts because it is essentially a threaded linked list. So I have quadruple which you have the number of characters and the actual letters which make up the word. The characters plus letters is used to detect the word that you're looking for. And then you have double which is used by quadruple and so on. So at the end of the day, this thread kind of devolves into a tree-like structure but we'll maintain the notion that it is a thread. Okay, so I need to find a word. So what do I do? It's like in any other linked list. Start at the beginning, keep looking. Either you find your word or you don't. Or you run out of words to look for. At that point your interpreter or your rep will say, I'm out, I don't know what you're talking about. No, no, it doesn't store in alphabetical order. It's yes, yes. So that's where I said the cleverness of the interpretation comes in. If your vocabulary is small, let's say you're writing it for a mass rover. You don't probably care about search so much, whereas you do care about the memory footprint. But if you're writing for a Intel chip with lots of RAM and whatever to back it up, then maybe you can write a clever algorithm and say I'll organize it in a particular fashion. But since, but usually from what I've looked and seen, fourth is targeted at these niche cases where you have esoteric chips or resource constrained situations. So you don't really want to optimize on speed. You'd rather do optimization on space, but that could have changed by now. The examples that I could find were targeted towards the optimizing of space. Okay, suppose I want to add a word. So go look it up. It's a fun word. That's why I just put it there. It's origin, origin is in South Korea. Shebaal, I think I'm pronouncing it right. So you want to create a new definition. So you have a head of the list pointer. You always attach to the head and then you essentially add it to the head of the list, rearrange all the pointers and you have a new word. So somehow you create a new definition and then attach to the top of the list. There is a catch. In fourth, everything can be overridden, not in the Java sense of overridden, but you can wipe out the definition of a particular word and replace it with something. So you have dup, I don't like dup. I'll overwrite it and use it for something else. Why? Because the entry, first the end, you start from the beginning. So if you overwrite something, the definition that is found there will precede or will supersede the others. Flip side. Imagine you have an old word which was written using the original dup. It will continue to use the original dup because your original dup is somewhere and the thread goes through your original definition onto the original dup. But subsequent words will use a different one. Tell me, are endless loops a good thing or a bad thing? Endless loops are. It depends on how you use it. Or is static typing? No, never mind. Or. But yeah, I guess it's a question of how you use it. It is a feature. I mean, you use it in a way you want to. By the way, you can wipe out the co-forth or you can redefine your co-forth on the go. The overwriting a word applies to every word. I mean, practically nothing is left closed. Usually built-in words are defined in assembly, but I have seen built-in words which are defined on top of the previously built-in words, meaning they're written in-forth. That's again like your normal language. It's a call that you take. Do I want to implement it in the underlying form or do I want to do it in my language? Okay, so you have a dictionary. How would you use it? Okay, what time do we have? 15 minutes to go, I'm speeding up. Feel free to stop me though. Okay, you have only primitives. Let's assume a very primitive dictionary where everything is in terms of assembly. Nothing is in terms of other-forth words. How do you execute something like that? That's where what we call direct-threaded code comes in. You have a series of instructions which point to something. And everything is in assembly. So you're executing it. You have a couple of pointers which point to the, you have a pointer called the next which tells the executor what to do next. And then during execution you switch the next and then you keep the point to the, or rather you have two pointers, one which points at the immediate next and another which points at the one to execute after that. Keep switching these and you'll be able to run through your chain of instructions. This is useful but limited. If your code is entirely assembly, sequential, with no run-offs to any direction, this will work. So how do you do this actually? So usually I'm using instructions for I 30, 32-bit Intel. So you have a form of load, load SL and so on and you have a jump. The exact instruction can vary based on your architecture. So you do a load which will push the current next one to the accumulator register and move the actual next register to the next one to execute. And then you say jump from where you're executing currently to whatever is in the accumulator and you're going to execute the next register, next instruction. So it's effectively you have just two pointers, keep manipulating them and keep jumping to one or the other and you're good to go. This is actually present in fourth because some part of the code is actually directed. It is actually assembly in sequence and that is usually defined as a macro called next. So if you compare various fourths, there's a good chance that you'll find a fourth primitive or an assembly macro which is called next. So every fourth primitive will have next so that it can go from one to the other. So you'll have chunks of code followed by next, chunks of code followed by next. But next is very lightweight, so you're not losing much. Okay, now I need J-Ball. Won't define my J-Ball. So your dictionary is not very primitive. It contains primitives and secondaries, secondaries which are, that is actually code written in fourth. So you come to indirect threaded code. So for example, let's take a double which is defined as dupe followed by a plus. So you have a code word. I'll come to code word in a second. You have the address of double, another double. And, sorry, this is four quadruple, sorry, double and double, the definition is up top. And then you have something called exit. You have the address of something called exit. The address of double, it points to the actual chunk where double sits. This is your thread. And double contains dupe, which points at dupe. But dupe is assembly, so you have written it in. You have written it in as a primitive. It is a built-in primitive. And plus is again assembly, so you have to go to a primitive. So this is your threaded structure. Now, I'll come to that in a second. In essence, if you're doing this in a regular or a normal language, let's say Java, it is essentially calling one method, descending into another, then into another, and then coming back up, which is what is happening here. The difference here is that you use those two pointers plus tags to effect that. You don't have, you're not operating at a programming language level where you say, oh yeah, I just need to descend an SN. I don't care about how it is done. Next operates in the way I mentioned. It's just that as long as those pointers are correctly set, after the next, you will land up in the plus. So you descend into plus, and after the plus, you will land up in exit, which will exit and take you back to the first double. Then you go into the next double and exit and come out. Yeah, yeah, yeah. So in this case, we have assumed that double is interpreted as fourth, whereas dupe and plus are interpreted as assembly. You can change that. Yes, yes, yes, yes, yeah, I'm sorry. Next is effectively, it is not an operation on the stack. It looks at two pointers. Usually the ESI register and the EAX register. One is used as a next pointer and the other is used as an accumulator pointer. In essence, the step I'm going to execute next and the step I'm going to execute thereafter. That is the usual definition. So it doesn't touch the stack per se. So the easy way to, one way to understand it is, look at the exits and next as roughly equivalent to your return statements in your regular language. The next kind of takes you to the next statement or next item to execute, semi-colon in your C or Java. Or it goes through the registers. I'm sorry, it goes through the registers. It's just that it has to go through the registers because you're operating at the assembly level. Exit is like your return. So next is your semi-colon in the sense that I'm done executing a line of thing. Please go on to the next one. And exit means I'm executing a function. But we don't have functions here. So that's the rough correspondence. It's not a one-to-one correspondence. So what is a code word? It's a code word is pointed to an interpreter to run the function. You have another, now we spoke about one macro called next. Oh, five minutes, okay. Seven, okay. So in primitives, the interpreter is pointing to the actual assembly, just like we saw. But in words written in fourth, you have yet another interpreter function, a small one, which is again a set of, I have a stack, I have a few registers. Let me manipulate them to get into a function and out of a function. So you have quadruple, which is calling double. So when you are at quadruple, you save the instruction pointer. You need another stack for this. This is where your next stack comes in. That's your return stack. Because I'm going to descend from quadruple into the next level. I need that pointer somewhere because it's not flat anymore. I'm nesting now. Then you point, then you create a new one, you point at your double. I'm going in there. Once you're done, you throw away the top one and then restore the next instruction, okay? So to, so when you descend into double, your stack top changes. When you exit the first double, it returns to the next double in the item. You can do this cleverly in a hundred ways through assembly and that's what a fourth has done. So you have a return stack and you have an interpreter function, which is usually called do call or call in or something because you have that call in operator when you are defining functions. That's a normal name. I mean, you can name it anything you want to. This is a very, very simplified version of do call. I'm sorry for the font size. It's a screenshot. So you have a pushed macro which pushes to the return stack and you're pushing the next register onto the stack. Then you're adding four, four because the word size is four. If your word size is something else, you would be adding a whatever, that particular word size to the thing. Then you're saying move it and then call next. Remember what next does. So you reach that now start executing there, please. So this is a kind of pictorial representation that I could think of that made sense of what we just discussed. In essence, jump from double into do call and then you have added four to the accumulator. So you have the next one in the five minutes. Okay, sorry. So this is your dictionary redefined. So you have do call and do and plus and exit. Does this make more sense now? I hope, okay. So you have next do call and exit together. These three form what is called the inner interpreter because that's what is keeping your code moving. It's called the heart of fourth. Then you have an outer interpreter which I don't want to go into it in detail. I hope you can see it. It is essentially a question of asking the user, do you have any input? Are we in compile mode? As in, is there a call and preceding it? If that is the case, please compile something. If not, try to execute it. How do you execute? You execute a search. If it is found, you execute it. When you execute it, if you have a stack error, you go back and so on. All these are written using fourth. So the entire thing is written in fourth. So if you want your outer interpreter behavior to change tomorrow, all you do is rewire those definitions and you're done. I mean, try doing that in any other language. In list to some extent you can, but it doesn't change the language. It only gives you a new replete. It doesn't in runtime, yeah. Okay, so you have start and restart. Start is effectively initializing the pointers, system variables, establishing, when you have some initial setup to do, making sure all the pointers are in the right place and so on. Token is effectively a macro which scans. It reads your thing, finds out what it is and takes it to the, if it is a valid thing you attach to the dictionary. Search, when you start at the top of the dictionary, keep going until you find the word that you're looking for. Execute, it has two operations. In execute mode it actually does it. In compile mode it effectively takes the word address and attaches it. Number is fairly simple. If it is a digit, it tries to convert it to the base system of that particular, whatever base system you have chosen and then if it doesn't, it'll throw an error. Okay, now we come to the interesting bit. So I need Erlang and 4th introp. I mean, I like Erlang after all. So hey, how do you do that? One way to look at this, let's say I have a C of 4th nodes which I need to orchestrate using Erlang. There are a few challenges there. The easy bit is that 4th is a program so I can treat it as a 4th node like you would treat a C node or a Perl node or a Python node which is working with Erlang. The catch, however, is that if I have a supervisor tree, for example, and I want to restart the 4th node, how do I do the restarting? What exactly is the state of a 4th node so that I can restore it to its original glory? So it turns out that defining the state of a 4th node is easy. All you have to do is you have a whole bunch of pointers, a couple of stacks, semi-assembly attached, persist it, and you have a 4th state. But when you restart it using Erlang, you have to validate that state ideally. Is it a valid 4th state? Because the 4th program could have crashed without leaving a valid state. How do you do it? I tried with some JSON which was ugly, but it worked. The ideal would be in 4th, have an exit valve called toErlang which will write the state of the stack with whatever into an Erlang binary term. When Erlang comes up or the supervisor kicks in, it says, oh, my 4th program has died. Has it left a stack, has it left a state dump? Read it, validate it. If it is valid, respawn, and when your 4th comes up, your start restart will read the particular state. And ideally, hopefully, it should be back in its normal state where it crashed. So you have registers, next accumulator, parameter stack, return stack, and dictionary. Capture these, and your 4th is there. Yeah, exactly. Memory is not the concern. You can have a very nice Erlang term or any binary format that you define which contains the whole thing. Try doing that with another language. Try doing that with Perl. You can't. Okay, to do. I have a bunch of things to do. One is, like I said, I tried playing with this, I couldn't complete it in time. Effectively, have a 4th word which converts the whole state of the 4th machine into an Erlang term. And then persist it into a file. Erlang binary format preferably. Dictionary persistence. Then I would like to play around with running 4th on different hardware. I just ran it on my laptop, regular Intel, whatever. But I would like to see how it behaves, how it works in other machines. And see how I can orchestrate. I've seen people running Erlang on Raspberry, so maybe it's not too difficult a task to coordinate the two and run them there. If you're interested in the topic, there are a whole bunch of nice books and material available on 4th. Leo Brody has written two nice books starting 4th and thinking 4th. Lolliger has written a very nice book called Thread and Interpretive Languages. Ancient classic, but I don't know if you can find a copy. Then Richard W.M. Jones has given an implementation. It's called Jones 4th. The source code is out there somewhere in GitHub. So, now we are open for questions. I can't think of one. I did it for fun. Good question again. Like I said, maybe there is a case where I have, there's a hardware crash, or my 4th machine has died due to reasons outside its control. Let's say I'm operating a robot arm and the arm got stuck somewhere. Maybe, I don't know. I'll give you an example. Yes, I do see some. I have never seen one in action out there in the real world. Chuck Moore and his company, multiple companies, which he has founded, swear by it. They have written 4th iterations over the years, several of them, and they have sold them and are doing quite well. So I would think that, yes, there is a use case, but I haven't seen it in action. What I would think is cases where you have a unique hardware and you want to implement something which is lightweight and yet is versatile without really having to dig into assembly. That's where it would come in. Thank you, everyone.