The honest way of Handling errors is to making it to the end user and telling the exact thing that has been happened over failures. Sometimes we can’t afford longer debugging sessions in case of mission critical things. The following phrases won’t help much in debugging when seen by users:
“Blah” went wrong!! (“What?
Your Transaction can’t be complete !! (“why ?
Not possible … (“why?”
Engine did not start… (“why?
Should not the above phrases have been informative. E.g. “Engine did not start because the Pressure tolerance is 0.05 exceeding to safe pressure tolerance 0.0005”
In this blog, we will talk about some monadic constructs in Scala and other scala based library which saves a lot of debugging sessions when it comes to failure. In this blog, how we would choose between them according to our need.
An Easy Example?
Let us take the example of validation. We always require at some point of our computation to validate input data. Suppose we have to take an address and pass it further if it is valid. Consider the following example:
Is “Option” a real option for the use case?
It is designed for a super simple use case. Sometimes you have some value and sometimes you have nothing. Suppose the above method is called with a value “some invalid address”
Here we’ve got following things not correct with our Code:
Unfortunately we can’t use None to process it further until we have a valid address.
When there is no error or backward communication, the users assumed that they had their “thing” done by typing an invalid address: A miscommunication.
The Ideal improvement at the moment would be the following modification of the current version:
Or If you don’t want to do this, you can call the previous version with a .getOrElse(“Default Value”). In fact, latter is the better option, because you have the flexibility to choose the Default value rather than hardcoding it as part of method. Now we are somewhat removed the first problem here but passing a default address for all of the customers would be the last option the users will agree upon. They still want to know what is the exact reason for the failure or What is not correct from the input.
Let’s Try to return the error.
Try[T] is another construct to capture the success or a failure scenarios. It returns a value in both cases. Put any expression in Try and it will return Success[T] if the expression is successfully evaluated and will return Failure[T] in the other case meaning you are allowed to return the exception as a value. However with one restriction that it in case of failures it will only return Throwable types:
But Throwing an exception doesn’t make much sense here since it is not much of a calculation. Although we can take this example to understand the use case. If the given string is not a number, it will be a failure. The value from the Try can be extracted in same as Option. It can be matched
The above can be replaced with below:
Try can be mapped over its values:
What if we don’t have to throw an Exception but return something else?
If you don’t want always to throw an exception in failure cases and make an exception/Error of your own, you can use scala.util.Either[A,B]. It gives you two type options A and B to return any information you’ve computed. Do not confuse it with tuple. It will only let use one at a time i.e. either scala.util.Left[A] or scala.util.Right[B]. Look at the enhanced version of zip code validation method:
By semantics, If you’ve declared the return type Either[A,B] then you are bound to wrap type A in Left and type B in the Right. Going by the scala convention usually you should Project your “Good” values in the Right and “Failures/Errors/Exceptions” in the Left. But don’t worry if you are left handed person! You can always do
What if convention makes you efficient?
There is another construct called Or which is simpler and more powerful than Either in the Scala. The Or is from the Scalactic library. It uses infix Notation as opposed to Either.
Or has a very good values vs error projection. Either it will return a value wrapped in Good or anything which is not Good is Bad. Bad will wrap whatever has been declared as part of return type in Or. Look at the enhanced version of the validateZipCodeWithOr method below:
Here is how I used it:
Sometimes giving a detailed error report after all inputs is very useful and saves a lot of efforts for users. Every in Scalactic library used to accumulate all the errors which can be summarized as a report:
Accumulating errors with Or
I could not resist the use of Copy and paste from Scalactic library to make you understand the use of Or for collecting Errors.
“Another difference between Or and Either is that Or enables you to accumulate errors if the Bad type is an Every. An Every is similar to a Seq in that it contains ordered elements, but different from Seq in that it cannot be empty. An Every is either a One, which contains one and only one element, or a Many, which contains two or more elements.
Here is a another variant for validating zip code with return type as Every.
Now let us Validate all addresses at Once. The below snippet uses Or Every for collecting all the errors while validating address. As you can see the return type it is: Address Or Every[ValidationError]
The Every[Validation] error will contain each error when validation fails of any address component e.g. street address or Zip Code.
The entire result will be wrapped in Good if all validations succeed while parsing address.
The Error will be returned in the following way if Any validation fails:
You can visit the library if you want to explore more about scalactic and other ways in which the errors can be collected.
Hope you liked reading the material. You are more than welcome for any suggestions!!