I really enjoy trying to understand how and why things like work, but for this post I’m going to try to skip all that wonderful stuff and instead give a practical outline of how to use a very useful pattern arising from applicative functors.
I’ve found this pattern incredibly useful in F#, Swift and Haskell. The examples here are in F#, but as far as I can tell we can use it anywhere that has generic types and higher-order functions.
Say we have some generic type, let’s call it
Widget<T> (we’ll use the term "widget" as a placeholder for a generic type we are working with - feel free to substitute in
List<T> etc.). There are lots of useful functions that work with non-widget types, and we would like them to work with
Widget values without having to re-write them.
We can achieve this aim if the generic type has a
Select in C# terminology) and an
apply function. Continuing our
If the type does not have these functions provided we may still be able to write them. We’ll look at this later.
We can use any non-widget function with widget values using
map for the first argument, and
apply for subsequent arguments.
Say we are using a library with a
Result<'Error, 'T> type that represents operations that can fail with a value of type
'Error, or succeed with a value of type
'T. The library also supplies
apply functions for this type. We want to use this type to try to parse a
Person value from a UI form with
age text fields:
When a generic type does not meet the prequisites
Sometimes a type will not have an
apply function provided, but will have
map, and also a
bind function provided with the following type:
This is the case with the F# Option module, which provides
bind with the required signatures. In these cases we can implement
apply in terms of the these other functions:
We can now use the pattern with optionals (and any type with
Mixing widget and non-widget arguments
In cases where we have a mix of arguments, some using our generic type and others not, we can still apply1 the pattern by converting the values to our generic type. For our
Person.create example, we could already have the person’s email as a valid
string value from earlier in the sign-up process:
Here we convert
string to a
Result<AppError,string> value first using the
Success constructor. Then we have our three
Result<AppError,'T> values to use with the apply pattern.
This pattern is useful for being able reuse all our existing functions in the context of another type, like
Result<E,A> and lots, lots more. To do this for some generic type
Widget<T> we need:
We then apply the non-widget function to the first argument using
map, and use
apply for subsequent applications.
Calls look similar to regular function application, with the additional operators taking care of conversion into our
We can mix widget and non-widget arguments by converting non-widgets:
I wrote a bit more about how this works a while back, or search around for "applicative functor" if you are interested in the theory behind the practice. We can effectively use this pattern without delving into the details though - so we can apply now and ask questions later. :)