 Ok, in... Ok, so maybe this should be part of a retro computing track, because it goes as far back as 1968. When Deastra sent a memo talking about why go to is bad, and why we should avoid it. And the memo was then redistributed by Niklas Werff under the name go to consider harmful. And that's the original source of the consider harmful phrase. Ok, so... What's wrong with the go to in the first place? Do we have to get some context? You can put it in different ways, but this particular way of putting it is shamelessly stole from Nathaniel Smith, who wrote a nice blog post about it. And it goes like this. This is a program in FOMATIC, which is the predecessor of COBOL, and it's an unstructured language. So as you see, there are no loops, nothing. It's not even invented. And you see a lot of jumps there. So you look at a program and you have no idea what's going on. And then you start drawing like arrows, like how the control flow looks like. And it looks like this. So you look at it and you have no idea what's going on. So what's wrong with that? You have different constructs, if, four, whatever, and go to... And some of them are good, some of them are bad. And how should we know which ones are good and which ones are bad? And one thing, one way of thinking about it is let's say the construct is good and the control flow starts in the top and then it ends up in the bottom. And if you look at all those constructs, like all of them have this nice property that just goes from the top to the bottom, if you do an if block, it can do the if part or the else part, but eventually it will arrive in the bottom. And same for four or function call or just sequential sequence of commands, but not so for go to. So it just jumps somewhere and it never returns. It never reaches the bottom of the code block. So we like to believe that the functions are black boxes. We call a function and the function eventually returns. Like nice and then easy to reason about. So if you have a main function it calls function foo, vision calls function bar, but eventually it will return from bar and it will return foo. It's also nice for encapsulation because main doesn't have to know that foo calls bar, because you change the implementation, it calls something else, you don't care, it still returns back. But try to use a go to somewhere and suddenly that's not longer a case. It just somewhere in the nested function it jumps somewhere else, never to return. Now here we have developer Bob, who is very cautious, he doesn't use any go tos, but he happens to use a library written by Alice, who is very cautious as well, she doesn't use any go tos, but she uses a library written by Colin who is careless and who uses a go to. And suddenly Bob is heard by that because like now inside the dependency tree just control flow just somewhere else and Bob's function call foo to foo never returns. Like all the assumptions he had about his code are broken. So this is kind of, it's not only like bad design, it's bad design plus plus because it's transitive. It takes only one dependency in your whole dependency tree and you are screwed. So what I was talking about was the old kind of go to that probably not many people here really remember. There were like big bad go tos which could jump around the code base from a function to a different function and you don't see those anymore. But it's actually not the long ago. When I started programming in 1984 I've got Sinclair's Spectrum and it had like built-in basic and it actually looks like much like the flowmatic code I've shown before. There were go tos all around the place and it just jumped around. So you don't see this anymore but what we have now is this kind of thing. Domesticated go to which can jump only inside a function. So that's the go to in C. Like you can jump inside a function but there's no way to jump out. So it's still bad but it's at least not transitive. So the call in, the developer of nested dependency can really break your workflow. OK, so now that I've explained the whole go to thing from 50 years ago what does it have to do with modern programming thing? And I'm coming from the networking background. I'm an author of ZeroMQ and I've made several networking libraries and the problem that occurs all the time is how to handle asynchronous calls basically. So this is what you have on wire. There is a byte saying that the following field is going to be 13 bytes long and then you have the actual beta. And how are we going to process that? So the old school way of doing that was like classic Unix way where you launch the process for each connection and the process was nice and sequential and it just read this, read that and that was it. But then people realized that launching a process per connection is probably not a good thing because it's slow and takes a lot of resources so we have to do better and so the threads were born and the interesting thing is about threads is you can't really find out who invented them but probably the person was to ashamed to actually... OK. So... So what we have to deal with nowadays is this kind of thing. We have this async function which looks like a function but it's not really a function because you would expect it to do whatever is inside the block and then return as a normal function but it doesn't. It returns immediately and then at some later point the async part is done. So if you just look at this naively as if this was a normal function you would expect it to print one and then to print three and then to print five and four and two but actually what you have is one, two and this part is executed three, four and then five. So it's weird. And yeah. And it gets reverse in JavaScript where basically those functions are anonymous and nested inside the code and this looks like a normal structure code but it's... it executes in very different way. So the layout of the program has basically nothing to do with the order of execution. So what you see here is one, three, five, four, two. What was printed out is one, two, three, four, five I guess. OK. The only thing to do it in is classic state machines where you remember what state you are in and the variable and well, this is at least explicit about what it's doing. So you go, you are in this state so you switch to a different state you will launch async call and some later point the function calls again with the reading done message and the other part of the state machine is executed but look at this code, it's terrible. And this is one slide but if you look at the real implementation it's like pages of this kind of stuff nobody can really grasp it to that first site. So then enter Golang. So the nice thing about Golang is that it introduced coroutines as a first class citizen and you are back to the old style of programming where you basically launch a process for each connection and it's nice and sequential except it's not a process, it's a coroutine which means it's cheap. Like this tag is at the beginning like two kilobytes and the context switch is pretty quick like 20 nanoseconds or something. So OK. So it seems that we are done but what's wrong with the go to? You see it kind of looks like the old go because in one way it just continues but in the background somehow the control flow just jumps away to some different function it's very similar to go in a way. So and you have the same problem as we had before. So once again Bob launches coroutine from Alice's library which downstairs is called from calling's library and calling does go and launches a coroutine which is running in background and the call returns to Bob's code Bob thinks OK the bar thing is done but there is actually a coroutine running in background and Bob has no idea. So it's sensitive again if any of your dependencies launches a coroutine in a background you have the old problem with this vile big bad go to. So how easy is it to fix? So one would say OK let's make coroutine scoped. So we have a scope, we launch a coroutine and when we get to the end of the scope the coroutine will be cancelled and that's it. Except that that doesn't really work because look here it is the classical code so we have a server which accept connections is handled by a handler which is a separate coroutine but if it was cancelled at the end of the block well it would be actually cancelled immediately and nothing would happen. So that's not good enough so what we really need is two different kind of scopes one is the normal scope and one is the better scope which cancels all the coroutines that were launched inside it. So this is in C this is a code using a library that I wrote called libdale and basically you see that the two scopes here there is a thing called a bundle it's a bundle of coroutines you initialize it in the vile you launch any number of coroutines inside it and once you want to shut down your server or something you jump out of the loop here at the break and you close the bundle and that cancels all the coroutines it's kind of you see it's indented like a nice structure so there are several languages that implemented this like this in python a library called trial which does the pretty similar thing except it's nice so you open a thing, they call it nursery which is the same as the bundle in the previous example and you basically do the same thing you launch the coroutines and once you get out of the block or the coroutines are cancelled so is that it? is that easy? so there are still some open problems here we are not yet there for example there is a difference between how libdale handles the termination and how trial handles the termination what trial does is you start your nursery you launch free coroutines and when you get to end the block, it waits it waits till all of them are finished and when they are finished it just continues if there is an error in any of them you just writes an exception and when the exception reaches this point it cancels all the other coroutines and then it continues libdale does it differently so there you open a bundle you launch some threads and at some point you say close bundle and at that point the coroutines are closed so the difference really is that in this case you have kind of like main thread which is in control of all other threads in the other case like all of them are created equal and and that there is no like the controller and the control so which way is better it's pretty hard to tell because in the trial version you have this nice error propagation where you throw an error and the error gets to the caller coroutine which is main but the problem there is if that happens then it kind of looks like that one of the coroutines can cancel its siblings and even its parents and we just very unstructured way of doing stuff so that's not nice about it so it would be nice to know whether we can combine the two approaches that we would have nice error handling and kind of nice encapsulation where coroutine cannot cancel its parents ok, so some other problems that are still unsolved or partially solved how would we how would we handle timeouts? so one way is to use something like as a golenx context where you have an object on which you can call cancel the timeout function and it will cancel after it or you can pass it manually to each function who knows what's better great spirit, that's a hard problem so for example you want to shut down your server and you say ok I am giving you like 5 seconds to finish of whatever stuff you are doing to close the connection decently and then shut down so we don't have a good story for that ok, I'm sorry to interrupt we're out of time unfortunately but we started a bit late so, thank you very much for the talk