 Hey guys, I jumped the gun a little bit in a good kind of way and want to talk about why. So in the last video I had explained that I was looking into and essentially experimenting with resurrecting collectathon, an alternatively designed collections library in a different way, going with a more trait-based API rather than an ADT-DS distinction. I'm really liking how that's turning out by the way, collectathon is definitely happening. It'll definitely get fleshed up to a point where I'm happy with it and getting a formal release. Number of things just from my own experiences working with these that it addresses. But I'm getting a degree of code sharing that I'm actually really happy with and this design allows for a degree of de-virtualization that is actually fantastic to where all of these collections can actually return value type enumerators, which little things, but they save on allocations and that's nice. So I mentioned that I was considering using collectathon as a base for helping with the increasing amount of collections that Stringer is implementing either for internal or downstream use. That's, I'm going to be able to accomplish that. I'm going to be able to use what I'm doing with collectathon as a base for that, which is fantastic. It's always lovely when you can implement an entire new collection type in less than a hundred lines of code and get full fleshed out functionality. It's wonderful when that happens. And then you don't even need to think about, oh, how do you implement something like index of and is this even appropriate for this collection? It's just, if it has the traits needed to compute the index of something, then that function will appear. Simple as that. So you like being able to go that route. That helps make sure that the Stringer collections are more fleshed out functionality wise. This is essentially like the same thing with using the orthographies by deriving them from the categories, because they still provide that behavior. Let's provide that behavior, make your types more powerful and all that. Now I do run into a little bit of a problem with certain types for Stringer. As I explained in previous videos, not the last one, but ones before that, there are plenty of cases where read-only span of car is ideal because it does an even better job of what string interning is supposed to do, because you've got even more sharing of memory by reference slices. There are certain data structures where you'd like to be able to utilize that, and it becomes tricky just because considering the limitations that ref structs have, I've tried to implement a tree-like structure using ref structs as the nodes internals, as the element in the node. It doesn't work. And that's because in order to take a pointer to something, it has to be an unmanaged type. C-sharp has gotten a number of extensions to exactly what unmanaged type is over the years, and as of the most recent, C-sharp 8, an unmanaged type is any of the basic primitives, like in 32, or car, or I think even string, but also any struct composed entirely of unmanaged types. And so it is actually possible to create trees who's entirely unmanaged types and just do pointers between them. Then you've got an entire data structure that actually exists on the stack-allocated memory, more like a traditional data structure rather than it being scattered throughout the heap. You typically don't want to do that, by the way. You can get barely any benefits from that at all, and it can blow the stack. So generally don't do that. But there are cases. There's a case, at least, that I've noticed, where that can be beneficial. And that's where you're using a string builder, but only within a single function. You're not passing the string builder around. You're not saving it and building it in increments throughout the lifetime of an application running. And I've experimented with this before, with implementing a string builder differently. And I got performance I was kind of okay with, but the performance curve was too complicated, too hard to understand, and it was clear that people, even myself, would have a hard time understanding when to use it and when to use the traditional string builder. I'm adamant, just from my own experiences, that there is an improvement that could be made, an obvious improvement that could be made, and it's just working out what the design of that is. I think I've got it. And it'd be a much more limited data structure. Implement the thing as a ref struct and make it super clear that this is a limited structure that is only supposed to be used inside of a single function as a replacement for the string builder. But we'll take a step back and talk about some of the generalizations, but still specializations that I want to be doing collection-wise. Part of the reason for looking into reviving collectathon also had to do with the desire to implement text-specialized collections. So that is, if you have, say, an associative array whose indexing types, whose keys, are not specifically a string, but just text, that you should be able to pass a read-only span or other things and get equality to just work without having to cast a thing to a string or do other stuff. You should be able to use any text type to pass it into that and to work. Similarly, you would want to be returning read-only spans of car instead of strings for many operations, especially since there are situations where you'd be returning substrings from a collection and obviously spans are far more efficient than actually allocating a new string for the substring. Very sharing. This is a highly specialized collection, obviously, and what the plan is going forward is essentially to split this into two parts, with collectathon being the most generalized collections suitable for anybody, and then having a more specialized collections API in stringier, stringier.structures or something, I'll come up with a name for it and have the specialized structures in there. Now there's a structure that anybody who's worked with text a lot probably knows of called the rope, and it's a type of binary tree that represents a dynamic string, and it does so very efficiently. There are other ways of doing this, of course, like the gap buffer and piece table, but the rope is one way of accomplishing that. And it's, I mean, it's efficient, and it's very good at what it does. It's meant for more long-lived, giant pieces of text, but again, it's good at what it does. So the specialized API would have things like that in it, and I would want to establish a good base inside of collectathon to be able to implement that in as little code as possible, as much code sharing as possible. But then as I'm going through and doing this today, I notice, actually, no, we'll get this out of the way first. The thing with the specialized dynamic string that would be a replacement for the stringbuilder inside a function specifically, the minuscule version of the rope, seems like that needs to be implemented as a dynamic array, so a resizing array where you swap the array size, copy the contents over. This sounds like it's a little inefficient, and it is for certain operations, but it can be actually more efficient than trees and stuff, depending on the context. Burnt out. As far as I can tell, there's nothing in literature that I've been able to find over the design for that I have, so I'm just going to be calling it a yarn, and so now we'll get into what I want to talk about more specifically. Both the rope and the yarn, as well as other text-like dynamic data structures, like the gap buffer, are all text, so it's a text-like. Now something that I've been doing with the v4 rollout for string here, and why core is taking so long, is that I'm trying to unify everything and make everything orthogonal to where any API that can accept truly any text parameter accepts any text parameter. Now is it really justifiable to delay everything to where I can implement both the yarn and rope? Just for the v4 rollout, implement all of these functions on top of the yarn and rope, as well? I don't really think so, that's an unjustifiable delay in my opinion. Notice something to implement for the future, honestly that could even be implemented just through a minor version bump, and I probably will do that, but it will be implemented regardless. So I'm going to hold off on that. Just a thought, I will still chip away at overtime, when I need a break from auditing and implementing tons of these redundant things, but I'm not going to be rushing implementations of yarn and rope until after the entire v4 rollout is done, so that I can then take those things and implement all of the APIs for each one individually, finish the rope and then implement all of them for rope, then finish the yarn and implement all of them for the yarn. So yeah, I'm still trying to keep the stringier v4 to be released at or around the same time as .NET 5, I want it to be released before this year is over. I will delay for any of the functionality required, but I'm not considering this required for the v4 rollout. So, that's where I'm at. Have a good one guys.