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.
Aim
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 Option<T>
, Either<E,A>
, Future<T>
, 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.
Prerequisites
We can achieve this aim if the generic type has a map
(or Select
in C# terminology) and an apply
function. Continuing our Widget
example:
If the type does not have these functions provided we may still be able to write them. We’ll look at this later.
Apply pattern
We can use any non-widget function with widget values using map
for the first argument, and apply
for subsequent arguments.
Example
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 map
and apply
functions for this type. We want to use this type to try to parse a Person
value from a UI form with name
, email
and 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 flatMap
/bind
function provided with the following type:
This is the case with the F# Option module, which provides map
and 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 map
and flatMap
/bind
):
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 email
from a 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.
Summary
This pattern is useful for being able reuse all our existing functions in the context of another type, like Future<T>
, Option<T>
, 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 Widget<T>
context.
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. :)
Sorry.↩