Is Maybe problematic? Well, maybe. Robert Peszek thinks that Maybe is overused. Cameron Gera and Taylor Fausak break it down and consider alternatives.
Episode 35 was published on 2021-01-26.
>> Hello. Welcome to the Haskell Weekly Podcast. I'm your host, Cameron Gera. An engineer at ITProTV. And with me today is Taylor Fausak one of my boss? Actually, he's not. He's on my team, but he's my boss as well, but we're just super excited to be here today. How you doing today?
>> I'm doing well, Cam. Thanks for hosting me on the show this week.
>> Of course, man. We're on episode 35. That's a big deal. So thank you all for listening. And we're hoping you'd like to stick around. So Taylor what are we talking about today?
>> So today we're gonna be digging into a post about kind of, maybe versus either and error reporting. But before we get to that, I think we have announcement from the community at large, not an announcement from us that we should talk about a little bit, which is the GHC 2021 proposal has been accepted. So, uh, yeah, very exciting. Cam, I know we talked about this on a previous episode of the podcast, but you got anything to say about it now that it's accepted?
>> I mean, I think I think it's cool I think it's interesting to see the data and how the votes went. As far as the committee. Selecting what? To bring in what? Not to bring in. Um, still for me. Personally shocked. Overloaded strings isn't there? But that's okay. You know, we can continue to enable that extension for us. It helps with our, uh, API Development.
>> Yeah, I think it's one of the most popular extensions. But this since it's the first cut of this new process, I think they tried to play it a little safe. And overloaded strings can introduce a lot of ambiguity. So I see why. Maybe they left it off.
>> That's fair. That's fair. Yeah. I mean, you know, they did great work with keeping it backwards compatible with Haskell 2010 and all that stuff. Um, but yeah. Do you have any, like, uh, just give ah, listener quick point of like, what's the difference between this and, you know, a language standard?
>> Sure. Once again, I'll point back to our previous episode in October, where we talked about this in a little more detail. But, um, this isn't a full blown new version of the Haskell language report, so It's not like a new standard. And the difference is that are, like the pragmatic differences that GHC 2021 is a language extension that just enables a bunch of other language extensions. So it's like an alias for these other things. Um, and a new version of the standard would be hopefully well specified enough that someone will be able to implement a different compiler that implements that standard. So most of the time, GHC is the only compiler that we talk about. But there are others, and there have been others in the past. And, uh, the language specification could allow more of those in the future.
>> No. Awesome. Well, thanks for that tidbit. We, uh, you know, obviously, like Taylor said, go check back at our other podcast where we talked to more in depth about that, or feel free to check it out in this week. This week's edition of Haskell Weekly.
>> Mm. So, but yeah, like I mentioned, that's not what we're here to talk about today. Instead, we're gonna be talking about maybe and either Right, Cam?
>> Yeah. Yeah, I think, uh, you know, for those who have been in programming and Haskell for a period of time. You've come across this decision of Do I use an either here or do I use maybe here and today the post We're gonna be kind of diving more into is, you know, leaning more towards Hey, use either here because you don't wanna lose necessarily information and I think we can easily kind of navigate that by, you know, I mean, it's really up to you know what the case is, right? Like everything's different. Nobody when you're, you know, saying for input like maybe it's generally OK for an input like it's either there it's not and you can kind of, you know, but that could also work for either, Like it's not there and it gives you some useful information. So, um yeah, I think you know, he kind of dives in here. Um, do you want to kind of give what his nutshell per se is?
>> Sure. And even before we get to the like, 30,000 ft overview of this, some of the motivation, so maybe is sort of, in a way, Haskell's answer to null most other mainstream programming languages have a concept of null that is a valid value for any type. And this is frequently referred to as the billion dollar mistake because it causes so many bugs and weird program behaviors. And in Haskell We don't have that. And instead, if you had something that would be nullable in another language in Haskell, you model that with maybe. And I think the high level overview of Robert's, the Post author here of his like what he's putting forth here is, if you are wanting to return, maybe perhaps you should return either instead, so that if something does go wrong, you'll know which thing went wrong rather than just like, Well, something somewhere went wrong. Good luck, Which is better than a null pointer error, but maybe only a little bit,
>> right? I mean, if you're parsing through some code and you're like, I could be these five possible, you know, null values or nothing values like which one is it? Um, you know, the fact that Haskell has the either concept really should allow us to at least consider it when we are choosing and reaching for. Maybe
>> I will say that in our day to day life, developing a Web application at ITProTV We frequently do both types of conversions where we have a maybe value, and we want to attach additional information to it in the case where it's nothing or we have some value that already has extra information on it. And we want to strip that off to get a maybe value. And usually this is to meet, you know, the various contracts of Oh, we need to turn a We need to give a maybe to this function and we have an either or vice versa. Um, but very often we have functions that give us back a maybe, and we have to add extra information there that says like, Oh, we were looking at this field and we expected this type and it was that type. But we got this value which didn't parse for this reason. And that's way more useful for us when we're debugging that problem than just getting nothing like something went wrong.
>> Who knows, Right? And I feel like, you know, there's a large Haskell ecosystem out there, you know, libraries and packages that help people solve problems. And, uh, you know, I think we've had people file issues and bugs on certain packages because this returning them, maybe. And there's some sort of failure that they don't have any idea to Why that happened on so You know, the author here kind of uses servant multi part as a, um kind of example to say, Hey, here's something that used to use Maybe now it's using on either, and it's honestly a better package for it.
>> Yeah, and for those that may not be familiar. Ah, multi part is a part of, like, an HTTP form payload. So whenever you have a form on a HTML page and you hit submit that gets submitted as a multi part and then that has to be parsed on the server And, sometimes it's JSON. Sometimes it's, uh, it looks like a query string. But regardless of how it looks, you're parsing that. And if it if you can't parse it, you want to know why rather than just returning a 400 to the user and saying Sorry, try again.
>> Yeah, I mean, that's not really giving anybody any information to go anywhere. I mean, you know, it's kind of that whole just putting somebody. You always telling that person to turn Right? And they're just gonna go In a circle?
>> Yeah, and then to kind of support this suggested shift into either versus Maybe, um, he proposes. Or maybe I'm not sure if he's proposing this function or if he's talking about one that already exists called unexplain for doing that conversion I talked about just a minute ago where you have an either. And you need a maybe, um And we recently introduced to functions like this to our prelude where we call them note and hush and, uh, may have come from somewhere else again, I don't know, but they're such simple functions. They're easy to write, so note takes it maybe value and the like, um, string. Normally that you want to give on the error and gives you back an either. And then hush goes the other way. So it's a little evocative of how they work.
>> It's very similar to unexplain, hush is.
>> Right and those are very nice because that you can use them in line. Really? You know, succinctly and works out nicely. Um, and I think he has an interesting example here of talking about Aeson the Jason Library, where it has a bunch of different functions for doing decoding that return various different. You know, is it an either. Is it a parser? Is it a maybe, is it? Whatever. Um, and it feels like you could just have one of those, and then these note or hush type functions, to, you know, massage it in the shape you want.
>> Mhm. Yeah. And I mean, you know, aeson's a good example of, a library that does have a lot of support for either, Um, whereas, you know, obviously they have Maybe, and I think that's kind of their default, but they've, you know, with the New Age and everybody realized Oh, yeah, we may want this error information passed along, You know, they've kind of added this either, um, either functions into their library. So much appreciated. I know. We used either decode a lot. Um,
>> yeah, we use that all the time, and I think their error messages have gotten a lot better. And I know there used to be a separate package for getting better error messages out of aeson. And so maybe they took a page out of that book and rolled it into the library itself.
>> Yeah, and you know, I think, uh, you know, aeson has, um, basic ways to signal a failed conversion, right? Whether it, you know, has that customary message with the either or just it's nothing right, which isn't always informative, and it kind of makes it a little difficult, But, you know, there's a certain type classes that maybe has that either doesn't, which I think we'll touch on here in a little bit.
>> Yeah, and like you mentioned with Aeson there are lots of different ways. It reports errors, and I think that comes from under the covers. It's like a parsing library. I don't know if they implement their own or if they rely on one that's probably attoparsec or something like that button. Those parsing libraries often, uh, implement either, well, one of the type classes of alternative or monad plus, and both of those have this concept of like choice. Give me either this one, or if that one fails, give me this other one, Um, and as a consequence, they also have the identity for choice. I think it's identity where you say this will always fail because normally If you have a list of choices, what do you do if none of them match, then you get back. Nothing. Um and that's very well modeled by maybe. And like you mentioned, it has instances for these things. But the error messages are pretty bad because, like with Jason, if you're saying I want to parse a number or a string or I didn't get either of them. So the error message you get is worthless. I don't know.
>> What do you really want from me, Right? You'll never know. Ha ha.
>> And there are things you can do to improve this too, like. You could make your last case an explicit error message that says, you know, I was looking for one of these values and I didn't get any of them. And I know that some parsing libraries, like Megaparsec do that more or less by default, where they smartly figure out what the next token could have been and show you all the options.
>> Right? Right, right. Yeah, Well, continuing on, I think we've come across this. Some in our own code is you know, the fact that you know monad fail and maybe don't really work well together. Uh,
>> or they do work well together. Maybe too well, because fail suggests that you will get the error message and maybe says, Yeah, I implement monad fail, but I just throw that error message away.
>> It's just like, uh, it's gives you a false sense of security, for sure or like assurance. I guess not. Security.
>> Yeah. You feel like oh, I'm doing I'm doing my part. You know, I'm helping us with exception reports by providing this beautiful, useful error message. But we just throw that away
>> Uh huh, Yeah, but yeah, and and kind of a related point is the next thing he talks about with cat maybes where, um, he suggests using partition either's instead, which lets you instead of throwing away the nothing's partition either's takes in a list of either's and gives you back a tuple where the left things are in one of them and the right things are in the other. This is a change we've actually made to a lot of the scripts that we write where initially we were using cat maybes and then either during code review or while we're actually using the thing we realize. Wait a minute. We're just dropping these values, like, maybe that's a problem that we need to know about. So we use partition, either's instead and then log them out or fail, depending on what it is. Exactly.
>> You mean, maybe maybe created some issues for us.
>> We got to get in at least one good pun each episode.
>> You know, I got to I mean so far, the listeners haven't thrown me out yet. So that's semi positive
>> Yes, maybe. may be problematic.
>> Uh huh. Well, awesome. Yeah. I mean, I would be in favor of that. I mean, the nice thing is partition eithers, you know, acts the same way. And if you're in a situation where you don't really care about what the lefts say, you can just, you know, continue with the rights and move forward.
>> Yep. Yeah. Pattern match on one side of that tuple And it's exactly the same as you called cat maybes.
>> Yeah. All right.
>> So the next thing he talks about is like a design pattern, and I don't want to spend too much time talking about the design pattern itself. It's called the higher kinded data pattern. And it's where, instead of defining a record with a bunch of concrete, um, monomorphic types for the fields, you parameterize the entire record over some container type. So the example they give is some person, and it has a type variable f. And for each field like name, it is of type F string and age's of type F int. And this looks a little goofy to start. But the idea is, when you're doing validation, you would have a person where f is maybe meaning that the values are either not there yet or didn't pass validation or whatever. And then once you've validated everything, you change that f from maybe to identity. And you're saying Okay, now everything is actually there, and this is, ah, powerful way. I know. In a previous episode we talked about, um, parse, don't validate. And I've used validation a lot as I've been talking about this, but this is a way to quote unquote parse this stuff by pushing this information to the type level and forcing yourself to prove that they're all there
>> mhm so. Okay, so with this higher kinded data pattern, um, you know, he kind of launches in with maybe all the way. Um, and obviously in this post, he's kind of saying, hey, be weary of maybe maybe it's not the best case. Uh, so he starts and kind of moves towards you know, I guess. Using either in this I got a little confusing that for me for a minute. Um, but what do you kind of parts of the next section of this higher kinded data? Strict pattern.
>> This one gets a little tricky because with maybe it's very easy to flip this around. So if you have a person where f is, maybe it's easy to turn that into a maybe person where f is identity? Because you try to get every field out of it. And if it works, you're done. Um, but often what you If you had a person where f is either string, you have a choice. Do you want to stop as soon as one thing goes wrong, or do you want to try to go as far as you can and collect as many error messages as possible and then return all of them? So the example I always think about here is if you're trying to sign up for some service and you load the form and then you just submit it and it comes back and says, Hey, first name is required like OK, cool. I'll fill out the first name, submit the form again. Last name is also required. Okay, fill that out and then, you know, do that three or four times you get a little frustrated, whereas if you just hit it once and it comes back and says all of these things are required, that's the difference that I'm talking about here. And he recommends a package called Barbies, which I had not heard of before. But it has a great description on Hackage. It says, Um, types that are parametric on a functor which is what we're talking about here. This higher kinded data pattern types that are parametric on a functor are like Barbies that have an outfit for each role. So maybe is an outfit. Either is an outfit, identity is an outfit. I just think that's a cute description of this. Um, but yeah, the package the Barbies package gives helpers for doing this error collection that I was just describing
>> Um, so, yeah, I'm not sure I have much more to say about HKD. Was there any other stuff you wanted to know about it?
>> No, I think that was good. I Yes. As I'm re parsing through it with what you're saying, Um, you know, the fact that it, you know, returns this either list of field info and your example was great. So I really appreciate that.
>> Yeah. Happy to help. Um, And then he, uh, he goes on the post author here. Goes on to talk about Java beans, which are I wasn't aware of this as, like, a thing. I have worked with Java, but it's been a long time. And, uh, apparently java beans air, like a class that can have an empty thing.
>> Yeah. Instance. Or like, a default value or something like that. Um, but I had a hard time following this section because I couldn't get these things to type. Check in my head with the GHC that's running in my head. And then I tried with a real GHC and it wouldn't type check there either, so I don't know what to say about this doesn't make sense to me.
>> Yeah. I mean, you know, one would think that, You know, if you have a person of the maybe functor... Right, functor? Maybe's a functor? Okay. I want to make sure I'm not mis-speaking here. Because I know someone on the Internet will tell me I'm wrong, but that's okay. Uh, you know, in person of identity, then you try to mix them together. That's not kind of work. I mean, and that's that whole compiler thing in your head. Not working. Um, I mean, if you wanted to, you could run, maybe and have it to, you know, return just 10 rather than identity.
>> Yeah, but then the whole example would be different.
>> So a little, a little more to be desired. Here. Maybe there's just something we're missing or misreading. Um, so yeah, uh, no, I think you know, that section was informative, but still a little confusing. So we'll keep moving so. When we were kind of prepping for this show, we talked. We kind of stumbled across this next section, which is the maybe first monoid fields. And we were also a little like first And then we realized first was just really a wrapper around maybe, Um uh, and you know, then things got really interesting when we, uh, made a grandpa toddler That happened somewhere later in this example.
>> Yeah. So, like you mentioned first is a new type wrapper around maybe. And the thing that it does for you is pick. Um, when you do like a semi group append that diamond operator or mappend either one, Uh, it will give you the first value. That is just something. So if you're one on the left, is a nothing? You'll get the one on the right, And if the one on the left is a just, then you'll get the one on the left, regardless of what the two values are. So it doesn't smash them together, like with strings or add them together, like with sum or any of that stuff. And this curious grandpa toddler value is where they take two people values and smush them together using this first concept. And you can end up with some weird data Where, uh, yeah, it's just like, you know, pretty much arbitrarily grabbing fields from one and the other and pushing them together.
>> At that point, you get a grandpa or you get a some person with the name Grandpa and an age of one. I mean, they must have started when they were really, really young.
>> Um and I'll just say for this we have done this in our code base, a fair amount where we have custom data types, that we have semi group instances to combine them together. But usually we implement those instances by hand rather than leaning on these, um, kind of convenience new type qrappers that the semi group and monoid modules expose like first and last and product and all this other stuff. Um, so this hasn't been a problem for us in practice. And also we don't implement them for, like our quote unquote business objects like person or whatever. Instead, it's usually for configuration or some type of aggregation that we're building up. So I do see that this is a problem, but we haven't experienced it,
>> right? Doesn't mean it's not out there. It's just firsthand, very little like, Yeah, it doesn't seem like a problem to us. But because we don't use that, so another big thing for you know that maybe has going for it is the fact that it has, you know, You know, we spoke earlier about having alternative instance and having more instances than either, Uh, which allows to be a little easier to work with, because you're like, Oh, yeah, it's got the instance I need here. I'll just go with maybe over either, rather than trying to create your own alternative instance for either.
>> Yeah, if you want to either write code, that is polymorphic or use code. That's polymorphic than it turns out that you can use maybe a lot more often than you can use, either. And I think that's a good segue into the next section. And almost the last section, which is uh, we've spent this whole time trashing. Maybe. But when is the good time to use? Maybe
>> Mm. Oh, good old. Maybe right. And, you know, we kind of touched on it in the beginning, right? Um, you know, the authors here says he follows a old design principle called Postel's Law. Where you know, lenient input strict output. Um, right. You know, things that can be put input to the system can be more flexible than what returned from the system,
>> right? So in this particular case, that means like using either for your input would be more strict because you would be requiring people to give you this error information. But with a maybe you're just saying I need to know whether it's there or not. I don't need to know why it wasn't there, so that's a little more lenient.
>> So go maybe woo!
>> I do think it's funny that he explicitly mentions data dot map. Look up as somewhere. That's probably fine to use. Maybe because for me, that's one place where I want either. And when I talk about in our code base, where we decorate, maybe values with error information, it's often on this where we're doing three or four look ups in a row, and we want to know which one failed. And it's really convenient to say, Oh, I couldn't find this key rather than just getting back nothing.
>> Yeah, maybe that's a future enhancement.
>> Mhm. Yeah, we'll have another. Yeah, well, we end up doing a lot of note. Look up. Using the helper function we talked about e
>> mean at this point we should just probably, you know, pull a new instance of look up into our prelude that does that
>> Yeah that has that on there
>> Yeah, probably could make life a little easier. But, you know, note. Look up. isn't the worst thing in the world either, but, Well, awesome. Yeah, I think that was, uh, some Good cases for using, maybe. And, you know, overall, maybe either is the way to go, but, you know, maybe has its place. That's why it's in the ecosystem.
>> This is a compelling argument to me, and I've found I haven't, you know, crystallized this opinion myself or thought too much about it before. But reading through this post, I was like, Yeah, you know, most of the time I would prefer something that dealt with either or something. isomorphic to either rather than maybe so that when something goes wrong, I'll know why.
>> Yeah. Yeah. So to kind of recap the author's causes for thinking, you know, for saying it's overused. Uh, yeah. He says, you know, using maybe is simpler than either, um, you know, coding with Maybe it's terser, which is a fun, weird terser uh, maybe is more expressive, really having more instances available to it on. And then you know, the sophisticated abstractions can can obscure the common sense. But, you know, use maybe it's gonna make it do what you want. It can make it do what you want. But you may also be missing information and losing out on things you expect to be there.
>> Yeah, I think this ties into the previous one where, since maybe has more instances, you can use it with more abstractions, and it will probably all type check. But you may end up with something that's very confusing either to use or to debug.
>> Precisely. Well, awesome, Taylor. Well, thanks for being on the show with me today. Thank you. Listeners for tuning in. Um, Taylor, where can they find us?
>> Well, uh, they could probably just google for Haskell weekly. That might be the easiest way, but if you want to go straight to the source, our website is HaskellWeekly.News We're on Twitter. Our handle is Haskell Weekly. Were on Reddit. Same handle were on github, same handle. And if you want to suggest something, to us, you could send an email to into@HaskellWeekly.News
>> They can't find us on MySpace?
>> We had to take down our MySpace page.
>> Just Yeah. Okay. That's okay, though, because Haskell Weekly podcast is brought to you by ITProTV the e-learning platform for IT professionals. And also our employer and Thank them for letting us do such an incredible show. Uh, they're also very generous because they would love to offer all of our listeners a 30% discount code for the lifetime of the subscription by using HaskellWeekly30 at check out. And that will get you 30% off the lifetime of that subscription. So go check it out.
>> Yeah, and I think that will do it for us this week. See you next week.