SBTB 2015: Long Cao, Evolving Your Code: More Functional Error Handling in Scala
Recording: SBTB 2015: Long Cao, Evolving Your Code: More Functional Error Handling in Scala
so you guys have been enjoying yourselves that's it's been a long three days for me so far i went to finagle con and that was really good that I'm like full of information and you know need to decompress at some point so here's another one so today I'll be talking about functional air handling and Scala this talk is more aimed towards people who are coming from say like a job a background or or like me I actually was doing PHP before this so so you come from a traditional like moresee like language and I just want to talk about one of the concerts that you can use to make your code more type safe and more referential II transparent and they're more reliable so going back to this like let's let's think about what is error handling let's define this right in within little and we're going to take the example of Java what does that mean it means usually with Ariel it means throwing exceptions and using try-catch to control the flow of any surveyors that you might run into Java does have the the concept of checked exceptions which mean you know kind of means that methods will tell you or in theory methods will tell you what kind of exceptions it throws anyone who has experience in Java knows that this has kind of been up like it was a good idea but the implementation and the actual usage of it was a pretty big failure in kind of a nightmare you're you can just catch any sort of exception and do whatever you want and it kind of breaks the little idea of type safety Scala doesn't have any sort of checked exceptions you can do whatever you want inside a function you can throw an exception and the compiler will not complain it will not say anything so throughout this talk I'm going to illustrate these functions and this domain using coffee because i really love coffee I think it is a very fun hobby for myself so that let's look at this function that I've written here I wrote it like someone who is like maybe first week of Scala they're writing something that is a buy coffee function you give it some money and you expect to get back a coffee object within the the body of the function the logic goes if you don't if you let's say you don't give it enough money which is three money three whatever your money being whatever currency you want to be lets say dollars it'll throw an exception spitting back an error saying you don't have enough money and sorry about that otherwise return a new coffee so we defined air-handling in the classical sense here well let's let's talk about what's functional air handling with in Scala or within any sort of functional language with with types it means using air handling types and passing in functions to the context to operate on the values within that type why would you want to do this it it helps increase referential transparency so then what you can do is you just look at the the signature of the function and you can tell that well this function can either be a an error or you get back the the type that you're expecting this has a whole bag of benefits and I'm just going to hand wave over this and this is something that is better left to I'm gonna talk but it helps increase correctness and among other things you can optimize around this you can cash you can cash function the return values and do all sorts of nice little optimizations and plus when you're reading it it and you kind of substitute the value in your head it returns back something that is always consistent so if you happen to call this function many times it won't change the the actual value so now that we defined what the two like the classical error handling and functionally are handling what I hope you get out of this is an appreciation for it error handling types are and why you would do it you get some compiler guarantees you get referential transparency you you can now just read the types of the functions and you can understand the general flow of a program the general gist is that if any of these types can flat map you can build your business logic around this you can start to chain functions and and never really leave the context of these air handling types and I feel very passionate about this topic because it's it's a very practical application of the FP side of scala you don't really have to dive so far into you know and i'm sure you've attended other talks but if terms like co eunetta like that i don't even know what that means but if terms like that like intimidate you you you kind of skip over this because this stuff is kind of a little bit further away from the crazy matthew side which is awesome but yes it's the deep end so i can talk about error handling types and and and the great thing about being further along in the conference is that i don't need to explain what monad is so I'm just going to breeze through this but the one-liner I like to give and this is the you know vastly vastly oversimplified explanations that it's a it's a box that has context lets you build computations on top of it and you can change things along the included link is probably my favorite explanation it's the one that kind of made everything click for me so now that we've laid down this foundation of functional error handling how do we go back to our original function of x coffee and and make it better how can we make it more transparent more informational the fur thing that I and I personally did this myself and the first thing you would do in this case is use option use an option type the option is a is a type that is either a some or none so either has a some value or it returns the type that has nothing but there's a little minor problem right does does the does now the function tell you what it why it possibly failed now you'd still have to actually look through the business logic dig through and figure out why what it was a nun in this case you look and see and it's the same kind of deal with you youth you'd see the if money is less than three then it's a nun we can do better than this instead what you can what can use is try it's a construct that comes from the standard library that try represents either your computation was successful or failure or a failure in this case what the failure state is is signified by an exception being thrown inside the whatever block you have this is this is a nice second step because you can enclose in a that has a useful message for why things failed and also it has the the nice benefit of rapping over code that that maybe exception leading exception Laden so especially if you're working with I don't know Java libraries or or or code that Scala code that someone else wrote that is very exception happy try is a really good thing to just shove in there we can as a third step we can go even further than that we can use either out of the scholars standard library either is it represents a what's called the disjunction or a disjoint union it's it's a wrapper of one of two types in this case we define it as either a you know a failure reason or you get coffee it's very similar to how you throw an exception inside the business logic except you're the possible outcomes are captured in the type system by by convention people have adopted that right bias means it's a means it's the value that expect left indicates an error however you cannot flat map over this because the Scala center library either implementation is is not biased so you'd have to pick a side and you'd have the enforces in your in your own code somehow there are very various solutions to this one that I can think of off the top of my head is using an implicit class to force a right projection you don't have to stop at just this Scala center library there are most of the functional libraries out there provide a implementation for these you can you can use scalzi disjunction which is a right bias disjunction it's pretty much the same thing as in either within the Scala center library except you can flat map over it because it is a it is right biased there's also the fantastic little library called school actic I don't know of any users of it but it's along the same DNA as Scala tests I think it's actually extracted from there but it has a really awesome construct for disjoint unions it's it's actually left biased and what this the nice little benefit is that you can write it as infix notation and the type signature is really it's almost like English because it becomes the good value or the bad value and that was probably the most clever you know type hacking that I've seen in a while but that's one option to use it also flat maps except it's left biased and also out of the the new library cats there's also xor which is another right bias disjunction also pretty much the same exact thing as scholars a disjunction the the common motif with all these types is that they fail fast right what happens when you chain flat maps computations on on top of these within the context if it decides that let's say with with try if it sees that the the previous context was a failure it will not run your next competition it just passes along up until the end of the call chain and you get back the first failure it's it in this way it kind of works is similar to an unhandled exception where you get an error somewhere early in your in your call chain and that's the first thing that it gets returned nothing else gets executed the the the nice benefit here is with the working with the Scala syntactic sugar of for comprehensions you can still have this fail fast behavior so this is a bit of a larger example here with of using a for comprehension with or and I chose this one because I i just wanted to showcase how the how awesome this type looks but i expanded the example to be about purchasing coffee and and underneath are several other steps the first which you have to do is buy some beans right and then and and then what you do is after that you brew the coffee and in this case I've I've you know just abstracted away the logic but let's say that buy beans has if you don't give it enough money you're going to fail and you're not gonna have enough money to buy beans and with brew coffee let's say you have a really really crappy grinder or coffee machine and a fourth of the time it fails to make a cup of coffee so you have these you have these functions that kind of represent in a real-life business logic right there's not not every function in the world will will pass especially you know it's it's it's especially applicable to anything where you might be talking to a third party you can't ever really rely on that third party to be a hundred percent up all the time so in this case where we're treating the the act of making coffee in the same way so I behind the purchase coffee function I am weaving the two functions together as as a for comprehension first to buy the beans you get back up some beans you take that result and you feed it into brew coffee and what you get yielded back is a or containing either coffee or the failure reason coffee with being a good value or the failure of reason to be in the bad and in this case you can see that when you run the the the purchase coffee function if you don't give enough money it will cough up the first error and even if you happen to give enough money the the second function may also fail so it spits back the failure reason so despite being the the easiest what I think to be the easiest introduction to functional libraries there are caveats to think about I think a lot of a lot of people like to you know talk about the benefits of these libraries but there's also some costs that as with anything in engineering not everything is a there's no such thing as a free win if there is someone belong to you so one thing that isn't very talked about is minding your boundaries within within libraries it's I wouldn't consider it very nice to expose any sort of novice user to what would be maybe like a scholar Z disjunction I mean I know for sure for myself when I first saw it I was extremely confused because I thought it was a some kind of operator that I I didn't know about also try googling for it you you won't what these what what this hat what happens is if you happen to start using this you bacon a hard dependency to certain libraries which may or may not have different implicit implications for you right so let's say that you have a team of mostly Java developers who are you know coming onto Scala and you've got the you know the one hardcore Haskell guy who wants to bring in scalzi and use all these crazy constructs that the rest of your team will not understand that's something to really think about you know that's that's of course work this is more of like a management thing but don't think of it as a free win but think of it as a possible good first step and this is something that my third point in this slide is something that I have been meaning to investigate for myself and would would really want to see the actual hard numbers is that performance numbers like art do you take a hit by you know boxing values into these types do you see what kind of things you give up to get more compiler safety as always you know make your make these decisions on your own or or do your due diligence and I think that you know the benefits obviously outweigh whatever whatever cost but don't go into it blindly so a lot of this talk has been was actually inspired by specifically this chapter in the the fantastic functional and programming book I think you know the first half of this whole this whole slide deck was basically based off of the chapter four I really recommend reading this book if you if you're interested in what it means to really apply FP and Scala it does probably the best job in in taking some some just a raw idea and actually implementing it and taking it all the way across the finish line so that was just basically a a short introduction to what functional error handling is like before anyone asks there there is another side that's a you know functional accumulation and that's what's what's what's known as validation yes and that is a whole other half of this talk I've only got 20 minutes but if you're interested about that there is more and actually wrote a little blog posts about it as well I did not include a link but any questions yes I have them on slides calm right now oh that's right i was asked about this slides i have them hosted on slides calm right now i'll be emailing a PDF copy to the conference organizers i think they'll be posting the slides and the videos somewhere yes right so I was asked why I put more emphasis on disjunctions rather than standard library constructs like try and actually try is a the perfect example of what I think is more of like a transitional type and I didn't mention this in my slides but I think what happens with tri is that if you keep it around for a while people will continue to use exceptions as part of their as part of their error handling tool box so I think it's more of a good way to insulate yourself from from exceptions but the longer you keep it around the more people will keep using exceptions and that's kind of like one of the points that I wanted to make was that you know try and lemonade exceptions don't rely on it but encode your exceptions into the type system use some kind of ADT that that can represent errors yes in the back I'm sorry what was that so the question was our exceptions okay sometimes so let's say you know if you're thinking purely then then no it's not okay but do we live in oh you know we live in a gray world so it it will especially if you're interacting with say like Java libraries or or jdbc or whatever exceptions will happen the the point that I was trying to make was that you want to insulate yourself from from exceptions wherever you can so in the case of you know some kind of code that throws exceptions try and use use try to wrap around that any less questions thank you very much