 Well we got another non-comparison video. So I'm gonna be talking about stringier and sort of where a lot of it stands right now. Considering I've had some related side projects and I want to talk about how all of this goes together. Hi, Brucey. Got one of the, uh, where is he? Where is he? It's so hard to do this. There we go. He's gonna be, I think, trying to check out the other cat, but he's the little kitten. He's pretty good about it. He's a rather fatherly cat. The big angry gray one sort of became his adopted son. So he, um, he's generally much, much better about it. Just kind of, you know, necessary caution. They're cats. They're super territorial. They're all males, but I'm not too worried about him. So anyways, stringier, the side projects, how it all goes together. I mentioned recently about the goal-directed framework that I've been tinkering around with. There's a lot of implementation details that need to be worked out, but at the very least a common error per thread has been implemented. So that's, that's a key part of it. That's actually what controls the entire goal-directed execution pipeline. So that's significant. That, together with another very small project called Defender, which is, for the most part, a guard clause library. Very simple stuff. The checks, the parameter validation that you do regularly, those are guard clauses. And a guard clause library is just a bunch of small, always inline to methods that do those checks for you. It's a little bit less typing. It's more of just a convenience than anything else. But it's there. Now my guard clause implementation is fast enough that benchmarked against handwritten guard clauses. It's equally as fast. And that's because it's not doing any special fluent interface or garbage like that. It's just the exact same code, but with a single method that you can call. That's convenient. These two together have a lot to do with general code safety. They are really applicable to just about anything. But for a large and increasingly growing project like Stringer, the easier it is for me to do that, the better. Now, Stringer, I was originally thinking that certain advanced types that it's adds in, like glyph, should be sitting on top of the others, that they should add extensions relevant to the others. I no longer feel that that's the case. There's now two types that I've added in, the glyph itself and a rune backport. And there's a few others I have planned. Things like a type to represent ligatures, which goes beyond even the glyph type. Some other examples. But I'm going to go through and make sure all of these extend exactly what they're supposed to within each library. When they do extensions, does that mean they have to depend on every single part of the library? Or do I do extensions per library? It opens up a whole can of worms that I really don't want to deal with. But there's another way to go about this. See, when you're tackled with immense complexity, oftentimes just reworking how you're doing things will address the complexity. You don't always have to create a system for managing that complexity. You don't always have to justify it. You could change the fundamental way in which you're doing things. One such way that I have after having gone through quite a lot, I feel that this is the ideal way for addressing things in my situation, is each library just has the bare minimum for that type. This makes a lot of sense with the Rune Backport. There's nothing else to add regarding the Backport itself. But doing so with other types such as glyph and ligature and others will enable the libraries to be put at the bottom of the dependency hierarchy. That is, they don't depend on anything else within string here. This means now that you have a single type. That's it. Ultra small library. Maybe some helpers. Glyph needs a specific helper called variant string. But for the most part, these are individual types. The main libraries meant for consumption can then depend on these individual types. Add in the extensions as relevant for those types. And in this instance, they actually wouldn't even be extensions. They'd just be the methods declared right along with everything else, which then helps make sure that you're actually implementing them. All of them. Because that's been a repeated problem dealing with so many different things. How do you make sure that everything is implemented and has feature parity with each other? Well, that helps. When you're forced to see them all right next to each other, it makes it pretty easy to tell when you've forgotten something. Right? Kind of have it a little bit of a stare down, but it's like barely a stare down. To the point that one of them is cleaning himself and the other one just kind of like smelling around the room in general and is actually walking away. So that's that's good. They're getting used to each other. They're not freaked the hell out by each other. That's progress. I'm hoping the fat gray one will be able to, well, I'll say that another way. I'm hoping this one will be able to essentially model appropriate behavior to the fat gray one. By the little cat and this one being fine, fat gray one will see that. That'll be problematic, though. That is a very grumpy, incredibly territorial and insecure cat. So is what it is. Hey, Bracey. Back to what I was saying, though. That puts everything, everything right up front and center, you can tell when you didn't implement something that you were supposed to. This inversion happens to also work well as far as the amount of stuff you include goes. See, the overwhelming majority of the code as far as this stuff goes is the method, not the type declaration. So adding in additional methods for Glyph, which actually wind up being super simple, because Glyph internally is a string, ligature would actually be internally a string as well. That's the best way to implement both of those types. They're constrained by specific semantics. But for performance reasons, that's actually the best way to implement those types. It's kind of funny. But because they are internally strings, that actually means the best, you can, I want to say best, that actually means that you can immensely share code between everything by just using the method you already have for string. You just wrap it back up in another glyph after having converted it. Because it's already a string, that means there's no additional allocation during this process. And because the types themselves are structs, there's no additional heap allocation. The discard is remarkably light weight. That works out to me. So that that explains how I'm reorganizing the whole dependencies within string here. There's another factor. However, that is no you're not going in there at all. The other thing, of course, I've already adapted string here to the use of defender. There aren't too many instances where the non guard clause parts of defender are utilized. And to, as of this video, the only non guard clause part at all is the control type, which is essentially a sort of a controlled inspired thing, but that actually uses the net interfaces. So it still implements I disposable, but there's a whole pattern around it that actually makes it considerably easier to work with the proper I disposable pattern. Essentially, you're given three methods to override and you override those specific things as necessary. And the actual pattern that makes I disposable safe is internalized, and you don't have to mess around with that shit, which is handy, very, very handy. Also makes it really quick to implement the actual pattern. But the guard clauses have all been used extensively throughout string here at this point. Having actually gone through and having used defenders clauses in numerous additional situations, numerous new methods written over the months. It's definitely easier using a guard clause library to make sure that you've written all your checks. So that's that's super handy that makes your code safer overall. Eventually, I'd like to add more stuff to defender certain analyzers, whatnot, enforce certain useful behaviors. But it's a later project. And some of those won't apply to string here at all. But that that being said, the gold directed framework, which I've been calling gold direction net. It's obviously just came out haven't adapted anything yet. And this is a big change to how code gets executed. You now run into situations where code can literally be skipped, because it simply doesn't need to run. There's already an error, you just bypass the code that that substantially changes things. With that in mind, I'm going to do another major version bump all of the string your libraries. So that puts us up to 4.0. Arguably, I could not do that, but certain interfaces are going to change as a result of this. And that is really when you should do a major version. Sorry, not not sure entirely what my cat was doing. But the fluffy brown black one, look like he was having a seizure. But as it turns out, he was just sort of like shaking something off of him. So he doesn't have seizures normally. So it was a little like what the fuck is going on. But no, he's fine. So updating everything is going to be interesting. But it's also going to be an absolutely fantastic opportunity to benchmark the effects of goal direction and make sure that the framework is operating as fast as it should be. That's going to be a little tricky. Considering how the h result property is exposed, I'm not entirely sure I will be able to do that as fast as I would like, I might have to come up with a different approach from the one I had described. But this, we are talking about using this and benchmarking it in a very real world situation. Those are the benchmarks that you actually want. That will allow me to get information about the overhead. And not just the overhead of setting an error, but also what impact it has when you outright bypass the execution of certain methods, because that's the thing that arguably makes goal direction worth it, even from a performance standpoint. It's not just that there's an additional error value. And then I mean, that that obviously has additional overhead. The two lightest ways to do that, neither of which I can really do from within a CLS compliant.net languages is to either use a register, which you don't have access to. It's a stack based virtual machine to store that in which case it's convenient and that it's just storing the error code. You would then have a table of these error codes, but look up the setting is very efficient. Similarly, you've got returning a additional value procedures return one, write them, use them exactly as if they don't return any functions return to write them, use them exactly as only return one. They are being returned implicitly. That approach does work with stack based virtual machines. And where I'm not wanting any compatibility between the existing CLS compliant languages. Well, I could go that route, but that that wouldn't allow stringier to be written that way, I'd have to go through and rewrite stringier and technically Langley wouldn't even work. I'd have to write Langley, have everything written with Langley to utilize stringier as it is before or currently use Langley to write a full general purpose goal directed language and then use that to rewrite stringier which then requires rewriting Langley in that language as well and rewriting the entirety of stringier in that language. And that's just holy fuck. No. No. Obviously, immensely better to work out some kind of common shared goal directed framework. So like I said, adapting stringier to use goal direction.net will allow me to get very important information about how it is to use in a non goal directed language. And the performance implications of this. That also is potentially paper worthy. There's very little research on goal direction. And most of it just covers introducing the syntax and how the execution pipeline differs. It's not a whole lot when it comes to how this affects performance. It's hard to say. Like I was saying, it does add a little bit. Every single method call has a little bit extra going on. But then you can completely bypass the need to execute certain methods as well. It's really hard to say. Regardless, I will be doing this. How exactly the goal direction framework will look by the end of it. I don't know. But this adapting it to a real world situation is the single best way to make sure that the user experience is appropriate that it works the way that I intended to and that it is performant. So this is going to be a fun project. Seriously, that's not sarcasm. Like I actually incredibly curious about this. This is super interesting to me. I definitely going to enjoy this experience. It's putting me outside of my comfort zone. Definitely. I've gotten very used to non goal directed things. I loved my experience working with icon and Unicon, but they're old and crafty and badly need to be updated. Even Unicon needs to be updated. Go all these neat. But there's no I have reservations about it. And hey, if you've got a cls compliant way of doing this sort cls compliant way of doing this. If you've got a way of setting this up to where cls compliant languages can still communicate with it. That's fucking awesome, because then you can do bindings between entirely different execution models. And we're not talking about changing from procedural to functional, where they're practically the same behavior. Goal direction is radically different. Henry, get the hell out of here. I'll throw this coffee at you. Oh boy. Yep, I'm probably throwing the coffee at him. Nope. Nope. He already knows what I was planning. So he ran off. That's good. The hat I hear you grumpy bastard. I'm enough of a grumpy bastard. We don't need two of us. This is good to go to experience. He's got to gradually learn to. He's got to gradually learn to be able to stand his own, how to address these kinds of issues. He's, I don't know how long he was on his own. It was definitely a while. But I think, I think that's all I really need to say about this. Till the next video. Have a good one guys.