Easy Validation with Scala's Either
This is becoming a well covered subject. But I feel this is so awesome I want to share my insight.
Context
I'm developing an OAuth login process for my play2 app: www.trendytics.com
OAuth requires some round trips to exchange information.
Furthermore I am incrementally fetching information.
So play 2 has this awesome interface to fetch urls with post/get variables:
WS.url("www.google.com")
You can then further compose this with headers and parameters, make a GET or POST request, transform it to json, etc.
At one point I needed to make 3 of these url calls each of which requiring information from the previous call.
I kept getting errors in them.. so I toke a different approach
Enter Either
Either encodes two return types. Traditionally people put Errors and Successs in them but you can put whatever you like/need.
So, with Either you can encode successes and errors.
What makes it interesting? Well, you can make this:
type Error = String
type Success = String
def call(url:String):Either[Error,Success]={
val response = WS.url(url).get.value.get
if (valid(response))
Right(response.body)
else Left("Response is not valid")
}
This returns a Success or an Error (both Strings in this case) if a valid function does not accept the response.
Left() creates an Either instance with the left element assigned (in this case an error).
Right() does the opposite.
In practice those instantiate Left and Right classes which are sub classes of the Either type, but are treated as Either
Furthemore you can even add exception handling to that..
So what, you ask?
Well, now we work with the Either
Each caller now has the ability to further refine the Success value or return an Error if it fails - which will fail the whole operation with an error message
Example:
def example(parameter:String):Either[Error,Success]=
for (parameter1 <- call("www.site.com/parameter").right;
parameter2 <- call("www.site.com/"+parameter1).right;
parameter3 <- call("www.site.com/"+parameter2).right) yield
(parameter3)
And this calls each url and combines the three calls where each is only done if the previous succeeded.
In summary: combining three errorsome calls, without losing data nor exploding in the user's face, into one beautiful package.
Finally, we can return the success or show the errors messages if there are any. But the processing in between didn't need to know about that.
What I'm doing is folding it:
example("parameter").fold(treatIfErrors(), treatIfSuccess())
These functions return the same type. Fold can then be used to unify your processing results, treating errors and successes as required.
How do you do it in your favorite programming language? (without if and elses)
Written by jaime jorge
Related protips
3 Responses
This looks like a monad. For the curious readers, I would suggest reading what monads are and look at the Scala's Option monad and see how it makes your life easy!
Indeed it is! Either itself it is not but when you call the .left or .right methods you create projections which can then be treated as monads (exactly like Option).
You should check out the Try type to replace either for error handling. it has Success and Failure and try is biaised for success while either is unbiased forcing you to use left and right projections