Rooftop Ruby Podcast
Two Ruby programmers (Collin Donnell and Joel Drapper) discuss Ruby, web and native software development, technology, and more.
Rooftop Ruby Podcast
12: Literally a Ruby Gem
Collin is finally back in Portland — it only took 6156 miles of driving! Meanwhile, Joel made another gem called Literal. Oh, and Collin might be entering a new era.
Follow us on Mastodon:
Show art created by JD Davis.
Joel, I'm back.
Joel:Welcome home.
Collin:I'm, I'm in Portland. I made it, uh, I had to drive through. When, when, where was I? The last time we talked, I was in, yeah, one of those states. Uh, I was in Minneapolis, Minnesota. So then after that I had to go through North Dakota, which. I don't know if there are any proud North Dakotan listeners here. There aren't a lot of people there, so I think there might not be. It was not my favorite. It kind of sucked the joy out of me because there are states which are very empty, but they're also very beautiful. Like Wyoming has a lot of really nice rock formations. Uh, When North Dakota made that choice, they selected like none of the above. Like there's just not a lot there. And there's also not, uh, a lot to look at. Uh, I did, at the very least, I'll put this in the show notes, get to stop at the statue of the largest Holstein cow in North America. So that was very exciting. Um,
Joel:gonna really offend a listener here, aren't we?
Collin:At least maybe one. At least one. The one who lives in North Dakota. Uh, so I had to drive through all of North Dakota, uh, stayed there, it was fine, and then continued on, got to Montana, drove through Montana. I stopped in a place called Bozeman, which is where they make Gibson guitars, which are my favorite kind of acoustic guitars. Uh, unfortunately the visitor center was closed, so I just took a picture of me kind of standing in front of some place where people work. I guess. Uh, I'm probably not the only person who's ever done that, and then drove to Missoula, Montana, which is very, um, it's a little bit. Like Austin or one of these places where it's kind of a very, uh, progressive city in the middle of a very conservative state. Uh, and I, I, those places are usually pretty cool, honestly. Uh, and so I really liked Missoula and it's only really eight and a half hours away by car. So I'm not gonna be driving anywhere by car very soon. But when I do want to, I'll probably go back to Missoula. Uh, and then after that, I literally just booked it. From Missoula to Portland because I just wanted to be home. I was, I was just done with this trip and, and, and now I'm here. I think it was approximately 61 56 and a half miles. So it was, it was a lot of miles.
Joel:God, that's a lot.
Collin:It, it was a
Joel:I can't even imagine
Collin:I'm. Absolutely never doing anything like this again. I don't know what was wrong with me. I think I was in a manic state a little bit when I thought this sounded like a good idea. Um, excited to have seen all these places and to have met, uh, so many kind of people in different places. Uh, I would say, you know, I asterisk a lot of kind people. I am someone who presents as, uh, you know, Heterosexual and also, uh, very white. And so who knows what my experience would've been like had, you know, that not been the case. Um, but for me, everybody was very kind and everywhere I went was great. Uh, but yeah, I'm never doing that again. It was such a long drive. Uh, but, uh, but, but you know, you, you learn a lot. You return changed, you know, like a hero, like Luke Skywalker. Uh, so that's me now. I battled the, uh, you know, I, I battled the, the ancient evil and I've, I've returned change. I mean, there wasn't actually an ancient evil, it was just, uh, it was more of an eternal battle. It was one of those movies. But anyway. How has your week been?
Joel:Um, my week's been good.
Collin:I saw you, you, you made a, you, you made a whole new library. I think you've made 17 libraries while I was gone.
Joel:think, uh, quite that many. Um, yeah, I did, I made, I, I started working on a new library this week, and it was kind of, it's kind of a combination of a, a bunch of different ideas that I've had. Um, and also bringing in, uh, a library that I wrote quite a while ago, uh, called Literal Enums. So it's gonna kind of fold that in. Uh, but the library's called literal and it is basically, it's essentially a, a, a kind of simpler take on dry types, dry initializer and dry struct. So, um, so provides a bunch of kind of tools for working with, uh, kind of type checked, uh, init, like building type checked Initializers. So, uh, yeah, there's like a basic. Kind of, uh, literal attributes is the kind of the basic tool and it allows you to kind of specify the attributes for your class. Uh, you can specify the name of the attribute as well as the type and it will define Initializer. And it will also define, uh, option a writer. Um, and. Uh, also, yeah, you can, you can also define a, a reader as well for, for that attribute. But basically the idea is that you can kind of not only simplify your initializers, um, but also like get a layer of type checking at the boundary for an object, if that makes sense. So where, where public interface is writing attributes, writing values to something you can make sure that those values are what you expect them to be. So it's not like you can never get to an application being type safe like this. Like there's no way to do that without, you know, knowing the type of literally everything. Uh, but you can, I think, uh, with a little effort, get a lot of like extra safety, if that makes sense. Just by kind of putting, putting this around the boundaries.
Collin:Y. Yeah, I mean, I think just having it around the boundaries and like on your initializers and stuff is probably the most impactful part you could have. Right?
Joel:Right. Yeah. I, I, I think so, especially with Rub, like there, there's no way that you're gonna fully type. I mean there, there are options like, uh, sobe, uh, which I know that Shopify's using with, with a lot of success. Um, But, uh, and, and I think, you know, we've got, coming up, we've got rbs, uh, which is Ruby's, um, kind of official, uh, type, type signature language.
Collin:Yeah, I would love to get somebody on, uh, who works on RBS at some point now that you mention it. I think that'd be a really good episode.
Joel:That would be really cool.
Collin:I think they could at least answer a lot of questions for me, so
Joel:Yeah. I've s I've spent a ton of time using rbs. Um, I wrote. RBS signatures for, uh, a few of my libraries. Uh, and I never got to the point where I could, I could type everything and then have the type checker. So, uh, I don't think there's an official type checker, but there is, uh, a type checker called steep. Um, and it looks like it's really, really great, but it's just not featured complete yet. Um, I think it's getting there. It's getting much closer every time. You know, every few months there's another release and it gets closer. But, uh, it ca for example, the, the thing I keep running into is, uh, it doesn't, it's not able to work with passing a splat to super or I think,
Collin:Mm-hmm.
Joel:uh, passing a splat to call doesn't work either um, but anyway, back to, uh, This library. So I, I think, yeah, you can, you can get a long way by just saying like, I want to do some basic type checking at the boundary layer, uh, for, for all of my objects. And so this first tool just lets you do that with like regular classes. Um, then the other thing is literal struct. And this is kind of like a ruby struct except instead of, uh, Instead of defining like just the attributes, you can define the attributes and their types and it will just type, check those attributes as they're created.
Collin:Right. That makes sense.
Joel:then the other one is literal data, and this is basically like ruby data, but again, type checked. Um, and the difference between ARU and a data object essentially is it the data is frozen, so. You can't mutate a data object once you've created it. Um, little data is slightly different to Ruby's data in that it will actually freeze, uh, the input. So if you, uh, if you give it like some attribute that's an object that's not frozen, it will duplicate that object and then freeze it. So it doesn't affect the object that you passed it. It won't mutate that, but it will duplicate it and freeze a copy of it. Uh, so that once you've got like some literal data that you're working with, you know that none of, none of that is gonna change, if that makes sense. Cuz it's, it's all frozen or at least one, one level deep. It's frozen. So everything you, you give, it will be either, either it's frozen already or it'll be duplicated and frozen and then it, the, it itself will be frozen. Um,
Collin:So it's like a value type
Joel:Yeah, sort of. Yeah. Yeah. It's a, it's like a value type. So that's what most people call value types. So that actually leads to the next tool, um, which I've called value, and I dunno if this is a great name for it. Um, because, because of the fact that people call these, like data types value types, um, But, and, and this is all very, like, very experimental, very scrappy at this, at, at the moment. But the idea of literal value is it's like data except it wraps a single value. So you might say, uh, define email address equals a literal value string, right? Or user ID equals a literal value integer. And then you can work with these like, like non primitive types. As if they were your, uh, you know, your string or your, your, uh, user id. And it means that you can, uh, kind of get a bit more from the type checking that we do. So if you have a job that's expecting a user ID, then, rather than saying, um, you know, it takes a user ID and the, and the type of that is integer, you can say it takes a user ID and the type of that is user id. Right. So it gets away from, uh, this pattern we call, uh, primitive obsession, um, and helps you to just set up these really lightweight, uh, types that essentially just wrap one value. Um, I dunno if literal value's the right name for that, but that's, that's kind of what I was going with at the moment. Um,
Collin:Yeah, I do get what you mean between literal data and literal value. Like that's where, that could be confusing, but I also don't know what else you would call that, like literal value does make sense. For the thing that you, uh, picked is this gem literally just called literal.
Joel:Yeah. It's just called literal. I managed to get that
Collin:how, how, how is that possible? It doesn't make sense.
Joel:No one had taken it.
Collin:my brain is broken.
Joel:Yeah,
Collin:The about text is good too. It's a literal ruby gem. It is literally a ruby gem
Joel:it's literally a ruby gem. No, no one had taken this, this gem name, which is amazing. So I was so happy to get it. Um, And then, um, oh yeah. So one more thing on the value types. Uh, and these are not like the data types, but the value types. They wrap One thing, um, I've been working on, uh, so basically when you create one of these types, we can do this case statement to check what type you've created it with, and then we can give you a value type for that type. So imagine, uh, I talked about email address and user id, right? So user ID wraps an integer, uh, because we know it wraps an integer, we can put, um, integer methods on it. So, uh, at the moment, all I'm doing is saying, um, You know, for these primitive types, I'm gonna have these like rapid types that you can subclass essentially. And if you create a value type for one of these primitive types that we know about, um, then it's gonna have some special features. So it, so user ID, for example, will respond to to_i. And return the value. Uh, the email address, if it's wrapping a string, is gonna respond to two s and two St r, and that's gonna return the value as well. So it means that, uh, if you have some other part of the code that is expecting, um, you know, a coercible string or percible integer, uh, then these values might just work by themselves without you having to unwrap and go to the underlying value, if
Collin:is so cool. That's great. I
Joel:Yeah.
Collin:And then you have a bunch of coming Soons.
Joel:yeah, so coming soon is, I'm gonna move the, the literal enums gem. I'm doing some work on that. I'm gonna move it into this gem. So it's just gonna be literal enum. Um, and this is for like doing, uh, like a constant enumeration of values. It's hard to explain, but, um, Maybe just some examples would be good. So say you have like a set number of statuses that something could be, or a set number of colors that something could be, or a set number of sizes. Uh, you could just enumerate essentially a constant list. And, uh, so you might have like the constant color, um, and you might have the constant color, red
Collin:Yeah. Red, green, blue as like possible types. Yeah.
Joel:And so you can, so you can, you can reference these things as, as the constants, but they all reference members of the class. So color red would be, uh, an instance, a frozen instance of the color class. Uh, and you can also give them value. So you could say, I wanna store these things in the database, and I would just want to use like a, um, you know, a tiny integer, right? So, Uh, I'm gonna say that, you know, status published is Status one, right? Um, and then it has some, some like integration with Rails as well. So you can kind of use it like, uh, like a Rails enum, except it's an object. It's not just an attribute of a model, it's an entire object, so you can put methods on it, right? So I could put a method on. In fact it comes with these built in methods. So, uh, the method, so, so if I have color red, I can send it the predicate red question mark and it'll return true. I can send it the pre predicate blue question mark and it will return false because, because blue is another. Uh, member of that enumeration, it knows for each of the members, I'm gonna define a predicate on each member, uh, that is able to like, tell me whether that member is the one that you're asking for. So if I grab a status, like say I have like, um, an article and it has like a status, I can then ask that status. You know, are you published? So I can say article status, published question mark, if that makes sense.
Collin:Yeah, no, it makes total sense because, uh, you know, rich enums of this type are a, uh, it's like a big feature of Swift, so I'm very familiar with this. Uh, so am I understanding right, so you could add like arbitrary methods to it then also, is that correct?
Joel:yeah. You could have had any, any method you
Collin:so if I had like an HCP response type or something, and I wanted to say like, you know, did this succeed? I could add a method like succeed, question mark or something.
Joel:Yeah. you could, or you could even add a method on the class. So you can either add a method on each member singleton, um, by passing a block when you define it. Or you can add a method to the class itself. Uh, so that's a great example actually. If you have an enumeration of HTTP status codes, you might have the names of those statuses, and then the value could be the actual code of the status. Um, you could define on the class A method that was like, was this a success or a failure? Right? And it could just say, it could just say like, is it a 200 status? And then you don't actually have to put that method on each singleton of the 200 s. You could just kind of define it and it can look at the value and know, um, you know, if it's above two hun, like 300,
Collin:Yeah. Is it in this range or
Joel:Is it in this range? Yeah.
Collin:Yeah, that's exactly the kind of thing I'm used to being doing Swift and it's a, it's a really great feature, so I think that's, that's super
Joel:So I, I just, I, I think it's a really great feature, a really great pattern. And you kind of don't, you don't have it in Ruby. There's no primi, like there's no primitive enum, uh, in Ruby and Rails. Enums are just like attributes of a model. They don't give you this. Yeah,
Collin:Yeah, no, rich enums are, are pretty, pretty great. Um, and then you also had operation and result in here. I don't know if you wanna talk about those or save those for later.
Joel:Yeah, I might save these for later. Um, operations are gonna be, uh, essentially my take on a type checked, uh, job type thing that can also, that can run asynchronously, but could also run, uh, synchronously, essentially a callable operation.
Collin:Uh huh.
Joel:Um, and they're also hopefully gonna be composable. And that will work with the, uh, the other thing, which is like literal result, uh, which is gonna be a monad. So a result could either be, uh, a success or a failure. But that's, but as data, rather than raising an exception, I think maybe we should come back and talk about these later. Cause I haven't like actually implemented them. But, um, yeah, they're all, they're all working progress, but, The last thing is, uh, all, all of these things kind of rely on the type system and they work by using the, the triple equals case equality method, uh, which is actually really powerful. In fact, uh, it's amazing what you can do with it. So as an example, the proc object implements equals, equals, equals to. Call the proc and pass whatever you give send to equals, equals, equals to the proc, which means any proc can basically be implemented to like, can just be used as a type in a type comparison. Um, you can actually use this in a case statement, right? So you could say case where, and then you just do a literal proc that receives a a value. Um, And does some calculation returns, true or false? And it will just work because Case just sends triple equals to the object. It's so cool. Um, yeah. Anyway, so you can, you can use like all of the normal types. So I can say, you know, I'm defining a person, uh, they have an attribute age and it's an integer or they have an attribute, uh, I don't know name, and it's a string, but, um, There are some types that are kind of missing in Ruby, because it's Ruby. So literal types implement some of these extra types. So the first one is union, and union is basically saying it's either this type or this other type. Um, so it, or in fact you can make a union of N types, right? As many types as you want.
Collin:I, I almost shot my hand up cause I'm like, I know c I can talk about that. Uh, but I continue.
Joel:Um, so then any type is essentially a union of all types apart from nil. Uh, and this is basically one you should never use, but if you need to for completeness. For completeness, it's there. Um, then we have maybe, and maybe is a union of nil and the specified type. So I could say like, this is maybe a string,
Collin:Mm-hmm.
Joel:or this is maybe, uh, an integer
Collin:So it's an optional, is what you're saying.
Joel:yeah, that's how you do optionals. So rather than saying, you know, it's a string and optional. True, you can just say it's a maybe string. Um, yeah. Uh, then bullion, uh, bullion is just, you know, union of true and false. In fact, it's literally implemented like this. So, uh, in the code I just def define bullion to equal union truth, false. Um, then we've got some other, uh, interesting ones and, and these are ones that are kind of expensive, but I think the idea is that, uh, I'm gonna build a way for you to turn these off in production. So, uh, array. There's a, there's an array type. Um, so you can obviously just say, this thing should be an array, but if you wanna say, this thing should be an array of this type, uh, this lets you do that. So I could say, you know, I have an attribute names, it's an array of strings. And it will actually enforce that every item in that array is a string. That's, that's where it gets quite slow. Like you, you don't wanna be doing that in production, but. It's the kind of thing that is fast enough to run in development, in test environments where you could catch a, a bug potentially. Um, and the idea is that you can, you can still use this library and you can still specify array string, but if you configure it so that when it's running in production doesn't do these slower checks, um, then things are gonna still be pretty smooth in production.
Collin:Yeah, because the default in Ruby, right? Cause you don't, it's not strongly typed. Uh, the default in Ruby would be basically as if you were in a strongly typed language that all arrays are any right, that they can just, you can put any kind of objects in it. And so this is a way to say like, don't do that. You know, do something, uh, make this like a string array or whatever.
Joel:Yeah, exactly. And because it's Ruby, this has to be done at runtime, unfortunately. Um, but you know, we're working with what we've got. So, um, then set is essentially the same. Uh, and enumerable is essentially the same. They just check that they are a set or an enumerable. Of those types. Uh, then there's tuple. And this basically is a way of saying, um, you know, I have either, you know, I have an innumerable thing and, um, at these indexes are these types, right? So it, you could say it's gonna be exactly three long, and the first thing's gonna be a string. The second thing's gonna be an integer. And the third thing is gonna be a Prague.
Collin:Yeah. Have you worked with tuple Very much, uh, outside of Ruby at all.
Joel:I haven't.
Collin:It's something I kind of wish Ruby would add, where you can be like, just return and you put in parentheses or whatever, and just say like, return these two things. And you can reference'em. They can be named, you can reference'em by index. If you haven't worked with that, you're probably like, why would that be more useful than something else? But, but it is, trust me.
Joel:Mm-hmm.
Collin:Uh, I, I, yeah. I really wish Ruby had that actually. It's one of the few things where I'm like, uh, wanting, I think that, and Richie Noom are actually kind of two things that I will oftentimes wish I could get in Ruby that isn't really a part of the language.
Joel:Mm-hmm. You, so Ruby does have something like, it has a tiny bit of a tule in it. Um, you can say, like, say you have a method that returns an array. Um, say the array is like 1, 2, 3. You can say A, B, C equals and then call that method. And each of those, uh, each of those variables will be assigned to the index of that returned array. So it's not like proper twos, but it does have like some niceties of tules, I
Collin:Yeah, that is a lot of what you do with tuple, so yeah, that's pretty good too.
Joel:Yeah, but thi this is basically just saying, uh, I have an innumerable type. It could be in a array, it could be a set, could be whatever. Um, and yeah, these indexes are gonna be these types and you need to be forced that, uh, same for hash. Hash takes a key type and a value type. Um, and of course, like you can compose these. So the key could again be a union, right? I could say my key is gonna be either a string or a. A symbol and my value is gonna be an integer or a float. Right. Just by saying, you know, hash of union, string symbol, and, uh, integer float. Um, then we have class, sorry. Then we have interface. Uh, and this one is a way of saying, I don't really care what type it is.
Collin:Oh, this one's really interesting.
Joel:I just wanna. make sure that it responds to these methods. Uh, so I can say like, the interface I'm interested in is two s, right? Whatever it is, I'm gonna call two s on it. So that's the interface I want you to enforce at this, at this layer. Um, unfortunately there's, yeah, I, I can't think of a way to do any kind of like ity checking at this point,
Collin:Mm
Joel:um, to like check, you know, is it twos, but. Expected to receive an argument or is it like just to us with no arguments.
Collin:Yeah, but so the idea with this is that it is, um, this would be really useful for if you had like duct types or something, right? But you wanted to confirm that you were actually getting set a duct, you know, a type that will respond to this.
Joel:right. Yeah. And you can actually create one of these interface things, uh, and you can give it a name. So you could, you could set a constant and say like, um, I dunno, my interface equals interface two s and then, um, or equals like union interface two s and string. Right. So you, you know, that you're getting, well, I guess string responds to two s anyway, so it's not, it's not a very useful one, but.
Collin:Yeah. Yeah. And in the example here, you, you show just, you know, the single to_s, but you can, uh, but it does take multiple, right? You could put, you could give it a collection of method names.
Joel:Yeah, so if you give it multiple method names, it will make sure that the inter, that the object responds to all of those methods. Uh, if you want to say like, I'm happy if it can respond to this method or this method, then again, you would just use a union. So use a union of Interface two s and two s t R, for example.
Collin:Oh wow.
Joel:You can just like compose them together.
Collin:How long? How long did you spend on this? That's so cool.
Joel:So far, like, I dunno, a day I think. But this is, this is a lot of the enums work is stuff that I've been doing before and a lot of this just came for free from Ruby. Like these are just the way that these work is, they are methods that return an object that responds to equals, equals, equals. It's that simple, right? So the method receives. Say, let's say we have a method called underscore array. It receives the type of the array and it returns, uh, an object type called array type, which has an initializer that takes the type and then it has an equals equals equals method that receives the other type. Sorry. No. That receives the value and it just says, um, you know, value each to go through each item in the array. Uh, or, sorry, it doesn't even say value each, right? It says value dot all. Um, and then takes the item and it says item, uh, sorry, type equals, equals, equals item. So like, actually setting these up took no time at all. Um, they're, they're so simple cuz they're, they're just piggybacking off of that, uh, interface that Ruby already defined.
Collin:That's so cool. That's amazing. Um, you're really good at programming, by the way. I don't know. I, I don't know if I say it to you enough, but this is wild. Uh, but you're just, every, every time I talk to you, you're like, oh, I made this whole other thing. Or I'll be on Macedon, he'll just be like, oh yeah, I'm doing this thing, whatever. And it's a whole new gem that does something crazy. I'm, I'm really enjoying watching you basically Reimplement Swift or Kotlin on top of Ruby. It's pretty fun.
Joel:Yeah.
Collin:Like those are useful features. I'm not saying like that's not cool. I'm just saying it's kind of fun to watch.
Joel:Yeah. I, I think the thing is like, uh, I think Ruby is, I. Great. And I love how dynamic it is. Um, I just found myself wanting to add a bit of extra safety at certain boundaries, if that makes sense. Um,
Collin:Yeah.
Joel:this, this is kind of a way of, a way of doing that. There's a few other types. I, I don't wanna go into too much detail. Um, and I'll probably add more types, but, but one of the last ones I'll mention is, um, so you can actually, uh, Let's see, how do I explain this? You don't just have to pass a class as the type, right? You can pass an instance. So I could say, um, you know, it re it takes, um, I don't know, a size and it should be a union of, and then pass the symbols, literally excess md, lg, right? Uh, And it's not matching. Like is it a symbol? It's matching, is it this symbol? Exactly right. Or is it a union of these exact symbols? And so one of the things you can do is if you wanted to say, this user has an age and they need to be 18 and above, uh, you could say the age is actually just a range. So you could say age 18, dot, dot. Um, but the issue with that is, Uh, that will match against things like floats and integers. So to narrow that down, I've added the type, uh, underscore integer, which takes a range. So you can still get that matching. But it also enforces that it is an integer, right? So if you wanna make sure, this is definitely not gonna be a float or anything else, it's definitely an integer. But also I want it to match this range. You can do that. I think that's the last, the last one I've done. We've done the same for float,
Collin:Yeah. Yeah, no, this, this is cool. Uh, I'm gonna, yeah, I wanna look at the code to see how you, how you did this. It's really interesting. Um, moving on. Uh, I don't have much to say, uh, but I, I. So I'm going to be doing some more kind of product oriented work. And I thought as, I don't know, because of that, I decided I was going to reread, uh, Steve Krug's. Don't make me think. And, uh, I think, yeah. and it's, it's a pretty good book. I think we should dedicate more time to it, but just starting off I'm like three or four chapters in right now. It's a pretty short book, uh, and. I've realized, I think I am in my, what, what, what would the, what would a Gen Z person on TikTok say? You know, they're in their whatever era. I think I am in my, just make things obviously clickable and with a good hierarchy era. I think that's the design era I'm in, uh, for myself. Uh, and may, maybe that doesn't make me very exciting, but I really like when things are obviously clickable and the sort of information architecture makes sense. So I think this is a really good book for that. I will probably reread the design of Everyday Things. Have you ever read that one?
Joel:I haven't, no. And to be fair, it's been a really long time since I read. Don't make me
Collin:Yeah. Maybe we should both read it and then we can like talk about it on a future episode. Uh,
Joel:Yeah, that sounds good.
Collin:yeah, so I'm reading that and it's, um, yeah, it's a lot about, you know, it's an older book, but I think it's, uh, I think it's still valid. So it's, um, he talks about things like, you know, make the opposite clickable, but then also the idea that people are not reading your thing as though it is a document they're reading. You design more like you're designing billboards because it's a user is more speeding by your website or whatever at 60 miles an hour. And they actually probably are not going to like meticulously figure out whatever, uh, you know, all the text to figure out the most optimal thing. They're going to more likely click the first thing that seems like vaguely related to what they're trying to do.
Joel:Right.
Collin:And I thought that was really interesting, uh, interesting way to think about it. So
Joel:a great analogy. Yeah.
Collin:Yeah. So I'll be, I'm gonna, I'll finish reading that. We can talk about it more, but, um, I don't know in the meantime, any, uh, any other thoughts, anything else we should talk about? I definitely want to, I think we're gonna try and start having more guests again now that I'm back, so that'll be exciting. Uh, we have some really good people I talk to, but, uh, yeah. In the meantime, anything else? Any other thoughts or,
Joel:Yeah. Uh, I'm trying to think what else. I've been doing. I've basically just been really just digging into this and trying to figure out how to, how to build generic monads, uh, in Ruby, which has been fun and not quite solved. Hopefully by next time, this time next week, uh, I'll be able to tell you that I found a solution to generic monads in Ruby.
Collin:And, and you managed to do it on the week of the coronation, which I know is probably a very. Uh, very sacred time for you.
Joel:Yeah.
Collin:Cool. All right, well, if you enjoyed the, uh, if you enjoy the show, enjoy the episode. Uh, please give us a thumbs up or a like, or whatever it is in your podcast app. Uh, tell your friends and, uh, we'll see you next week.