In this implementation of Functional Validation we are going to use Option.
Quoting Arrow doc:
Option is a container for an optional value of type A. If the value of type A is present, the Option is an instance of Some, containing the present value of type A. If the value is absent, the Option is the object None.
So basically is a sealed class with two different implementations:
The main idea is to wrap a value that can be present or absent. Wrapping this value in a container such as Option allows us to use this data without worrying to check each time if the value is present or not. We can just use some standard functions like map and flatmap to manipulate the content and write less boilerplate code.
Let’s use Option
We are going to use one Option to represent the mail, one Option to represent the phone number and then we will merge them to obtain the output of our validation
In our representation will use Some to wrap valid input and None for invalid input, so for example:
To obtain such behaviour we can use kotlin extension methods invoking validMail and validNumber functions we have discussed in previous post
At this point we have a Option representation for each field, but what we really want is to validate the whole form!
Map to the rescue
Our goal is to obtain an instance of Data when the form is valid:
but what we achieved so far are two Option.
Luckily for us there is a useful method that accept two Option as inputs, and returns whatever we want. This method is called map and we can use to build our Data instance
In this snippet we first wrap mail and phoneNumbers arguments to Option, then use map to merge them.
With map we pass as last parameter a lambda that takes as input a Pair with the values of the two Options. So in our case the lambda receives as input a Pair with two strings, the mail and the phone number, and returns a Data instance. (it.a is the first value of the pair, and it.b is the second value of the pair).
If we take a look at the return type of the validateData we discover that we are returning a Option of Data and not a Data directly, but we are not creating an instance of Option anywhere, so it must be the map method that wraps our result in a Option!
The real power of map method is that it allows us to write just the happy path in the lambda we pass and it will handle all other scenarios for us. If all arguments are Some (and not None) it will execute our lambda and wrap the result in a Option.
What happens when at least one input is None?
In this case our happy path lambda will be ignored and None is returned.
and exact same result for
Since we are getting None in both cases we can’t know which input is wrong!
Let’s use this code to build an android form.
I’m using Android Architecture Components, so the glue code will be in a ViewModel, while the activity will do just UI rendering.
In our ViewModel we will use the previously discussed code to update a
In the activity we retrieve the viewModel, register click listener and observe the result of the validation. Once we get the result we fold it.
What is Fold?
Fold is a method defined on Option that takes to functions, the first one will be executed if the Option is empty, while the second one if the Option contains a value.
In our case we are using method reference with the following methods:
As you can see in the second method we receive as input the value of the Option, while in the first method we don’t get any input. So, as previously discussed, using Option we cannot notify our user which field is wrong.
So, Option is not a great choice for validation, but don’t worry we will fix this issue in the next post using a different data type.