 I'm going to read this because later we equaled, but I don't know. So first, we want to look at three things today. Circuit? Yes. Here. First we'll look at what is wrong with exceptions in Haskell in general. Then we'll look at what is wrong with dynamic typing. Haskell or GPC exceptions are dynamic type. And then actually at least last we'll look at what is really wrong with GPC's exceptions. And actually the third point is the meaning of the talk. It's the shortest part, but it's the interesting part. So please follow me until the end. But let's start. Sunker? Yes. So what is actually wrong with exceptions? If I get different exceptions, it looks like that, right? Exceptions, something bad happened, but there's some crucial information missing here. So I don't see where it happened, right? I don't see what type of exception there's this. And if I don't know the type of exception, I cannot even catch it. Let's say I want to catch candidate, except I cannot easily. And how can I deal with that? I could not use exceptions, but actually that's a quite good idea. I think it's pretty popular in Haskell community to just not use exceptions at all. But if I use some third-party library that uses exceptions, that may not be an option. I could, for example, use greck. Where's that at? Where's use greck to figure out? Yeah? Yeah, I thought so. Everybody who uses some Haskell libraries that use exceptions eventually will use greck to find the type of an exception or try to figure out where this exception actually comes from. It's not as easy as just, not always as easy as just pressing for the message that you see there, because the message may be composed, right? I mean, this one we already know, right? It's had and can just, you know, this one is from previous, that we just need to grab an hour code where the views have most like some hour code because no views have. But we just look at all transitive dependencies and see who used that, right? If you define your own exception, right? I never do. But if you do, I'm advised to just derive the showing stance because then, when you see this one, this is actually just a show of exceptions, right? Then you see a constructor name, right? And a constructor name will give us something. What kind of exception we're dealing with? Make sense? Okay. And there's some good news about GPC 7.10.2. Like with the latest group of GPC, we have stack traces based on because of parameters. That's actually, yeah, good news. The bad news is it's not used for exceptions yet. But maybe in the future, it could not be for, like adding proper super stack traces to exceptions. At least have the infrastructure now in GPC. Okay. I lost the first part already. Second part, what is wrong with dynamically typed exceptions? So, exceptions are organized as a tree, right? At the top, at the root, we have some exception. And then, like, maybe one to one here is subtyping, like a job or a group, right? We want the hierarchy of exceptions, so we have at the root some exception, and then we have arithmetic exceptions, error code, assertion fade, then we have some ASIC exception. Yeah, below that, even that exception, or kind of other exceptions. And that thing is described in paper as an extensively dynamically typed hierarchy of exceptions by some model. Yeah, the interesting thing is, I can't quite, this paper is actually quite accessible. And it basically models subtyping in Haskell, and how it does it. It's like, if you have a hierarchy, if you have type, like, like fool, right? And then, we have a subtype, bar. And then, again, we have a subtype, bus. Then you would just, like, model subtyping by wrapping, wrapping constructors. So, we would have this bus constructor, and if you want to have a benefit of this one, right? I want to express that it's a subtype of fool, you know, right? Then we have this bus constructor, wrap it with a bar constructor, right? And this whole thing, we wrap with a fool constructor. This is how we, I mean, kind of, this is kind of a basic idea, how we sometimes, we also have theorems, nice. So, if you want to model this hierarchy, right? Some exception at the top, then here, a subtype fool. And then, again, two subtypes, bar and bus. Then you would have two existential types. Some existential, existential, that can wrap any type. Then fool would be existential, that can wrap types. And then, you would have a type, like a data type, bar and bus. In this case, just as a simple example, just only one constructor, right? And if you then, say, throw bar, we end up with an exception, with a value that looks like that, right? This is the constructor, bar, wrap the fool, wrap the sum exception, okay? So, yeah, this is the basic idea. Details are in this paper, right? The details are in this paper. We just specify the type, right? We could catch everything, right? Then we just specify the type sum exception. If you just want to catch bar and bus and everything that's below fool, right? We just say, yeah, let's catch fool, right? And if you want only, you want to catch one specific exception, then you just specify the type to be enough. So, given this, like, concept, can we decide where it sits in this tree? Is that possible? I'm sorry, it's not. Yeah, it's possible. Yeah, it is. We have this, like, fool we don't have, right? We have this sum, I think, exception. Yeah, we could. That was introduced in base 4.7, so it's relatively new to address some problems that we'll talk about later, or at least probably look at. But yeah, you could do that. You could wrap an error call in a sum, as an exception. Yeah, you could do that, and you should not, right? Exception of two exceptions, right? Two exception methods, and exception type to us. We don't look at it, but kind of they do the two exception for error call, it does not do that. So, it's wrong, we'll never do it, right? But yeah, it's dynamical type, and on a type, if you look at a head box, there's no clue how this tree looks like, right? If you look at Java box, for example, you see the exception tree. It has to, we don't see that tree, in a time, it's reflected properly. Can you accept a tree at outer leaves, right? In Java, the idea is a little bit like, I have some exception, right? I want the most specific exception. I just extend that class, right? That's how object-variant language will do that. Yeah, also this paper claims it should be extensible, right? It's kind of extensible, but if you look at this, could be extant, could be extented tree at far, but that's impossible. Yeah, okay, yeah. So, yeah, it's not very possible. Yeah, that's another thing, catching almost all exceptions. The common thing that I want to do is, say, yeah, I have a web server, for example, right? So sometimes if some request, for some reason, like some request kind of throws an exception, I don't want to bring down the web server, right? I just want to accept the next three questions of that one, right? So I want to catch all exceptions, almost all exceptions, but what about a heap overflow, a stack overflow exception? Do I want to catch them? If I'm running out of memory, maybe not. What if I get a sick term? Do I want to catch it? Maybe not, or I may want to append it differently? Or more interestingly even, I think, if I have an assertion fade, right? This is for development. Usually I don't want to recover from assertion faders like in production, assertions are removed from the code, right? If I compile the optimization, assertions are removed. But during development, usually I just want my program to go down, right? On what generally speaking, if you make exceptions, it can classify only two categories. There are some exceptions that I can reason and let me recover from, right? There are some exceptions that are just fatal exceptions that I usually don't want to recover from. So the question is, how can you catch all exceptions? Or how can you catch all exceptions by asking for exceptions, right? It's possible but not straightforward. Yeah, maybe you want to catch and save by asking for exceptions. And assertion fader, not fade. It's also possible but not straightforward. Yeah, what's wrong with that if you have exceptions, right? We're looking at the time of a specific exception I cannot determine where it says to treat. A value of some exception, let's assume I just catch all exceptions, right? And then I want to find out what exception is that actually, right? Then I cannot easily get the time of the exception, right? I know the some exception construct I can remove, and I don't know when I have the first exception, but I don't know which type to look for. It's also not possible. Users can't extend a treat in an arbitrary list. Yeah, also, asset exception and assertion fade sit in the same subtree as other exceptions. How shall I, for example, source that? Just remove that. Below that, we have an error, but the errors, but the incinerations are usually you don't want to recover, and exceptions put exceptions that I would usually want to recover from, and usually I would only catch that or something else. Right? And you could fix that in estimates, just the current situation you're stuck with. So how to deal with that? And we could not lose exceptions at all. And they made us just the easiest solution. If something's broken, you just use it or avoid it to use it. When I define my own exceptions, I would actually keep the hierarchy from that. I would put everything under some exceptions. Then there would be a solution, like for the first two points that we have here. Yeah, for asset exception and assertion fade, there's no really good solution. There's an interesting blog post by Michael Svoimer catching all exceptions. You could look at that. It's a pretty long blog post that provides all the particularities. But it also changed a little bit with base 4.7. We have this additional tree, right? We have all the asynchronous exceptions here under some asset exception. And I think as long as people don't do obviously stupid things, as you mentioned before, don't write other exceptions at that time, it would also help a little bit. That was number two. So now for number three, the major backup. That's basically GC. GC is interpretation of inputting exception semantics. Let's look at an example program. Like here we have a function F, right? And it is basically always rows and exceptions. People who are into program language are designed like... In academic, we could just call this function, right? This function is unifying, right? But if we have an asset program, we actually can observe difference between foo and bar. Even suppose there are exceptions, we could observe difference, right? And that's what we try to do here. If you run this program, what would actually happen? Would you see exception foo or exception bar? So foo is foo. Foo? Yeah? I mean, it's reasoning foo, no? No, it's not, right? Bar? Bar? What's the bar? So in this simple case, it will always be foo. But in general, in cases like that, it depends. It depends on the GC version, and it depends on the optimization level, right? And the connection does not give you proper exception ordering, okay? And I can't give you an example like that because these kind of things happen when you really can't use them. If you have lots of code, lots of exceptions, right? Then your component is O2, and then you run on your server, and then it fails. But I've got another example similar to this one, right? And this one actually is condensed, right? So what exception would we see here, right? So does anybody know what this dogar bang-bang does? Okay, it's basically from deepsec, right? That would basically force... It's basically like a version of the dog operator that deepsecs the string that we have there, right? There's a string that starts with an A, and after that it's an exception, right? So the height is the height, but the tail is not the height, right? Okay, and this dogar bang-bang is basically just a deep-secing version of dogar, right? So it should force the string, the value, and then return it, right? That would just throw out an exception. So which exception would we expect in this situation? I mean, I prepared that here, right? Let's just try this program. It's cool, right? That's expected, I think that's expected, right? If you force that list, right? Yeah, but if you force that list, then you return it, right? I mean, that's obviously how it behaves, but I'm not sure... I mean, it's like, there's another version that there's this dogar bang, right? It's basically like it does a sack, but it conceptually does a sack and then returns it, right? The dogar bang-bang says that if you have f-dogar-bang-bang-x, what it tells you is that the x will be fully evaluated when the f-dogar-bang-bang-x is fully evaluated. Yeah, and then, I mean, I would assume like the return that you have to buy an operator somehow, right? That they have kind of actually somewhere conceptually out of silk, and then it's sent here to be forced and then we only go to the next one at a statement, right? What makes sense? I mean, if the first one is a dream statement, you would assume the dream statement happens, even if you don't look at the reset, right? And then only the exception would be thrown, right? In the same way, I would assume the exception here should be thrown before we go to the second-throw statement. Yeah, I mean, at least it works, right? I mean, that's just understood, right? I mean, it works. Yeah, but, yeah, if I run this code with QC 7.6.3 and compile the code with O2, then it would be fine. It can also be like that, right? And then let's just put O2 here, right? And then it's fine. At least not, at least not, I mean, whatever the other could be, we would prefer like, yeah, deterministic behavior, right? And it's not deterministic, right? A more correct version, like evaluate.force, right? So we first force the value, and then use this evaluate, which is kind of special. This evaluate guarantees us exception watering, right? This is from control exception. But if you want exception watering in QC, you always need to use this evaluate, right? And sec does not give us exception watering, contrary to the problem we need. Also pattern matching does not give us reliable exception watering, right? So, I mean, this program may behave as expected most of the times, but it's not guaranteed. But people say exception and poor code are bad, right? For a couple of reasons, they're already bad, right? In section one, you already saw that we don't get useful information from exception. So exception and poor code are actually bad, but they're actually worse than you may think, for that reason, right? Because if you have multiple exceptions and poor code, you may actually see at one time exceptions that technically could never happen, just because, like, QC applies some code transformations that are valid with the assumption that all bottoms are basically equally... equally good luck, right? Like, if it's a bottom, then it just... we're happy with any bottom, right? I think there's no identity for bottom, but we just say they are all the same, right? Yeah, like, so we should avoid exception and poor code by all means, I think. I think that's not very controversial. It's also important that you always do throw I.O. instead of throw whenever possible. There were some bugs before in HUE, for example. Even so, the type was in I.O., you could still do throw, because where can you throw I.O.? You can always do throw. The right thing is to do throw I.O. If you want to force exceptions, then you always need to use evaluate. I mean, there's many broken code out there that just uses, like, deep-side in combination with return or something like that, but it doesn't work. You need to use the recap. It's actually really, really wrong with exceptions in Pascal. Yeah, we have no location information. That's hopefully to change. Yeah, we have dynamic typing with an indication that if you look at some exception, you don't really know where it sits in this exception tree, right? If you have an exception, you cannot get, like, if you have some exception, if you catch all exceptions, if you want to try to find out the type of an exception, right, that you see at runtime, then you just catch all exceptions, but then it's not easy to just get a time, right? You can only unwrap the first level. The next step is not possible. Not easy and possible. You need to do it repetitively. It's quite cumbersome. Maybe you can't, it's a little bit too strong, but it's at least not easy. Then there is actually a shortcoming in a number four. It's a shortcoming in a feature at the same time, right? You can't expand at the tree at least, but maybe you should not do it. You should keep the character flat. Yeah, let me have this dilemma. There's no easy way to catch only the exceptions that are reasonable to handle, right? No easy way to do that. Yeah, I think the nature of screw up, like, you may see exceptions in which you see that cannot reasonably happen, right? Because if you sack or, especially like pattern matching, we don't get ordering guarantees for exceptions. That's it. There are a couple of references. Yeah, this is paper N extensible, dynamically-typed party of exceptions. I mentioned that before. I think this is really approachable. I think it's really interesting this paper. I recommend it. Well, you read it there. I recommend you read it. But not a paper as semantics or due to these exceptions. I think the core of that paper is relatively simple, right? If you have a plus, like if you have an operator and they can pass the plus, for example, right? And y, right? x plus y, right? Then in a lace language, or it has to be known, you really have to define the variation order. If all of them are undefined, right? For example, this one could be error-foo, or this one could be just exception-foo, right? Throw-foo, and then this one could be exception-bar, right? That means to decide which exception to throw, right? And this name of anything says, yeah, if you have this equation like that, then you just say, yeah, I mean, we just say, this is exception, exception. And then at a point where we observe the exception, right? We just make an optimistic choice. We just take one representative on this side and represent it to the user. But I think this is fine. But the bad news is that GC does not really adhere to the semantics, right? There's no comeback. There's some discussions on this issue, but it's still open, and it's considered no priority because like fixing that would mainly hurt forms a little bit. To me, I would tend to fix it, but I also don't know what the performance implications are as we measured it. Yeah, there are a couple of other GC issues. It's mostly about discussions there. We can just get an understanding that it's a little deeper, but number three is pretty well covered there, right? Did the committee mention about the roadmap for location information? Haskell committee mentioned about the roadmap, for adding location information? Whether it's in the roadmap or in the release modes, or in the roadmap, did they plan to develop that feature? Or to add the source locations, to extract the exceptions. I don't know what the current state is there. I know there were earlier discussions that some people had concerns to use everywhere. When I looked at the code, I hadn't really spent much time on it, but there is some partial, basically every partial function, would be kind of special. To every partial function, we would want to add an interest parameter, that's the code stack, and then GC does some magic by building up this code stack. They mean functions like we have forward, R1, this one is partial graph, but this one would have an additional unconstraining. I think this is a parameter for the code stack. I would be worried, but maybe it's solvable, or maybe I'm just worn out for no reason, but I could mention that maybe fuchsia would break down for these functions. But maybe it's solvable, I don't know, but I think we should not use those functions anyway. I'm not sure, I think it would be good idea, but any other questions? With throw IO, you get also proper ordering guarantees. If you have two throw IOs, first try IO, let's assume you have IO code, then you have one statement throw IO full, one statement throw IO bar, then throw IO bar will never happen. If you're right, throw full and throw bar, then again you don't have this exception ordering guarantee. Even so the codes, you could say this is not a pure exception because it unifies to IO unit or IOA, but you don't have an guarantee. You may still see the second exception. Instead of using... An exception or a shortcut mode that does not have these issues. Yes, I mean other means of exception ways to express failure, like maybe or either or some shortcut mode, which basically are fine. I think that's what people prefer. In the Haskell community, do we have a spec for compiler implementation like for exception? In C++ word, it will probably be implementation defined or undefined behavior, but Haskell community just ignore this back or they have that corresponding... I think this is not covered by Haskell 2010. So in Haskell 2010, we don't have this like dynamically tied exceptions, right? But we have undefined in Haskell 2010, and they say at least you should... That is what Haskell 2010 says, you should add or it's expected that implementations should add some useful information, basic source information, right? We don't have that, we don't have that in Haskell 10. Like when it comes to other things, like impris is semantics for example, that's just this two papers, they're on top of Haskell 2010, so this is outside of Haskell 2010. So no, I think it's not defined in Haskell 10.