So I was happily going on with my Scala journey avoid exceptions everywhere. Then I ran into this use case where I sorta needed them. But I really liked not having try/catch all over the place mucking up stuff. So here’s this use case:
I’m in the middle tier, people call me via Rest, then I call sql, sprocs and more sprocs, Rest, your mom’s old couch, whatever and make that rationale and pass it back up. The thing is that Rest service I’m calling might poop out on me, and I’d like to just pass up the json it sends me to the guy above me. So I have this situation where I need to return two things.
I know exceptions can work here, I can return my object in the good case, or an exception in the bad case, but I really hate checked exceptions, not to get into a whole theological discussion about it, but I’m definitely in the camp of exceptions are best for exceptional stuff. That whole FinderException crap EJB started, pisses me off. Sun really went ape shit with that stuff. I digress..
So how can I return two different things. Well I could create some wrapper object thingy. Yeah that would work, but its so icky, and then, I’m going to have to down cast stuff, or have a bunch of icky wrappers. I don’t think that will work. Then I discovered Either.
A common use of Either is as an alternative to Option for dealing with possible missing values. In this usage, scala.None is replaced with a Left which can contain useful information.Right takes the place of Some. Convention dictates that Left is used for failure and Right is used for success.
This is the medicine I was looking for. I can return this, or I can return that. OMG, genius!!! What is even better, is that the convention is that the good stuff is on the right, and the bad stuff on the left. Now Scala is even jelling with my political sensibilities. Wow, mind blow.
So I can write stuff like this which is pretty fun:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// if there is something there Cache it | |
// clearly Martin Odersky is not a progressive, well or left handed 🙂 | |
if (entityEither.isRight && entityEither.right.get.isDefined){ | |
Cache.set(getCacheKey(entityName, lookupKey), entityEither.right.get.get, 60) | |
} |
So back to how I use this. When I call into my store like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def getEntitiesByKeyFromDatastore[T]( | |
entityName: String, | |
lookupKey: (String, String), | |
reads: Reads[T]): Either[DataStoreResponse, List[T]] |
I send back up an EIther. Which has a list in this case and then the raw DataStoreResponse on the left. So if something bad happens, you get the raw DataStoreResponse, else the object(s) you were looking for. So in my case where I’m a service sending json back up in the good or bad case, things end up like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def getExceptionTemplateByEnv(templateId: String) = Action { | |
Core.getExceptionTemplatesByEnv(templateId).fold ( | |
outputDataStoreResponse, | |
(templates) => Ok(Json.toJson(templates)) | |
) | |
} | |
def outputDataStoreResponse: (DataStoreResponse) => Result = { | |
(dataStoreResponse) => NotFound(dataStoreResponse.jsValue) | |
} |
That’s so much nicer that try/catch all over the place. But the thing is, this is stable, resilient code, it handles failure well. But it doesn’t hurt your eyes to look at, is very concise, and reads well.
Given a sequence of functions
f1
, …,fn
, return the functionf1 andThen ... andThen fn
.So read about partial functions and composition. I’m finding that learning the functional vernacular really helps you google stuff to find what you are looking for. As a professional day to day programmer, what I need is to make these tools part of my everyday toolbox. They aren’t just esoteric academic things, they are tools I can use to solve real problems, which is why I’m blogging about this, so others might see how I used this capability to solve a problem.