Trustbit

View Original

Functional Validation in F# Using Applicatives

This is my entry for the F# Advent Calendar 2019 https://sergeytihon.com/tag/fsadvent/.

I'm using Railway-Oriented Programming (https://fsharpforfunandprofit.com/rop/) and was looking for an elegant, functional way to handle the conversion of an UnvalidatedType to a Result of ValidatedType or List of ValidationFailures (UnvalidatedUser -> Result< ValidatedUser, ValidationFailure list >). Having read Scott Wlaschin's excellent book (https://pragprog.com/book/swdddf/domain-modeling-made-functional), I knew that Applicatives were the functional answer to this problem.

This post is my interpretation of what I found.

Initial Solution

We start with a couple of simple record types and a discriminated union of potential validation failures:

See this content in the original post

We then add a number of partial active patterns and functions to handle validating the individual values:

See this content in the original post

Note that the validate functions return a list of ValidationFailure in the Error case. This makes concatenating them together later on much easier.

We now add a simple helper function to create a ValidatedUser and the main validate function:

See this content in the original post

The last line is commented out because it obviously won't compile as the types don't match but it is an aspiration to end up with a solution that gets close to this.

Helper Functions

We need to create two functions; the first to handle the non-Result to Result mapping of 'create' and 'validatedName' and the second to handle the rest. The first is such a common requirement that it is built into F# Core (from 4.1) -> Result.map. It looks very similar to this:

See this content in the original post

This only works in this situation because of partial application and that is equally important for the Ok path in our next helper function:

See this content in the original post

The consequence of using partial application on the Ok track is that the partially applied function must always be the the first parameter to either function. This means that we can easily produce some working code to utilise these two helper functions that follow the rules to solve our requirement:

See this content in the original post

We can show that this works correctly by writing some example test functions:

See this content in the original post

Whilst our function works, it contains back pipes <| which Don Syme https://twitter.com/dsymetweets, the creator of F#, doesn't like, it is not considered the idiomatic way of writing applicatives in F# and doesn't really look anything like the result we aspire to achieve.

We can reduce some of the noise by removing all of the piping:

See this content in the original post

This is beginning to look like what we were originally looking to achieve. To progress further, we are going to use another of the really useful F# features; infix and prefix operators.

Infix/Prefix Operators

We have all seen the addition operator used as an infix:

See this content in the original post

but it can also be used as a prefix:

See this content in the original post

We can define our own operators:

See this content in the original post

We can replace Result.map with (<!>) and apply with (<*>) in our code:

See this content in the original post

If we now use the infix versions we get:

See this content in the original post

We then remove the unnecessary brackets and we finally end up with:

See this content in the original post

You can easily verify that it works by running the two tests we created earlier.

If the number of elements get too large, we can rewrite it like this:

See this content in the original post

I think you'll agree that this is an elegant solution to our original problem.

Putting It All Together

Let's finish off by showing the final codebase:

See this content in the original post

I love functional programming with F#. Nothing makes me happier than finding simple, elegant solutions to interesting problems.

Further Reading

If you want a deeper dive into the wonders of applicatives and a lot more, I highly recommend that you read the following:

https://fsharpforfunandprofit.com/posts/elevated-world-3/#validation

https://blog.ploeh.dk/2018/10/01/applicative-functors/

Finally

Thanks to Sergey Tihon for https://sergeytihon.com/tag/newsf-weekly/ and for running the F# Advent Calendar, Scott Wlaschin for writing https://pragprog.com/book/swdddf/domain-modeling-made-functional and making https://fsharpforfunandprofit.com/ such an amazing resource plus special thanks to all of you in the F# community for being so awesome. :)

https://twitter.com/ijrussell