An applicative functor is a useful abstraction. While typically associated with functional programming, applicative functors can be conjured into existence in C# as well.

This article series is part of a larger series of articles about functors, applicatives, and other mappable containers.

In a former article series, you learned about functors, and how they also exist in object-oriented design. Perhaps the utility of this still eludes you, although, if you've ever had experience with LINQ in C#, you should realise that the abstraction is invaluable. Functors are abundant; applicative functors not quite so much. On the other hand, applicative functors enable you to do more.

I find it helpful to think of applicative functors as an abstraction that enable you to express combinations of things.

In the functor article series, I mostly focused on the mechanics of implementation. In this article series, I think that you'll find it helpful to slightly change the perspective. In these articles, I'll show you various motivating examples of how applicative functors are useful.

You should consider reading one or more of these articles before continuing the present article.

A Haskell perspective #

A normal functor maps objects in an 'elevated world' (like C#'s IEnumerable<T> or IObservable<T>) using a function in the 'normal world'. As a variation, an applicative functor maps objects in an 'elevated world' using functions from the same 'elevated world'.

In Haskell, an applicative functor is defined like this:

class Functor f => Applicative f where
  pure  :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b

This is a simplification; there's more to the Applicative typeclass than this, but this should highlight the essence. What it says is that an applicative functor must already be a Functor. It could be any sort of Functor, like [] (linked list), Maybe, Either, and so on. Since Functor is an abstraction, it's called f.

The definition furthermore says that in order for a functor to be applicative, two functions must exist: pure and <*> ('apply').

pure is easy to understand. It simply 'elevates' a 'normal' value to a functor value. For example, you can elevate the number 42 to a list value by putting it in a list with a single element: [42]. Or you can elevate "foo" to Maybe by containing it in the Just case: Just "foo". That is, literally, what pure does for [] (list) and Maybe.

The <*> operator applies an 'elevated' function to an 'elevated' value. When f is [], this literally means that you have a list of functions that you have to apply to a list of values. Perhaps you can already see what I meant by combinations of things.

This sounds abstract, but follow the above list of links in order to see several examples.

An F# perspective #

Applicative functors aren't explicitly modelled in F#, but they're easy enough to add if you need them. F# doesn't have typeclasses, so implementing applicative functors tend to be more on a case-by-case basis.

If you need list to be applicative, pure should have the type 'a -> 'a list, and <*> should have the type ('a -> 'b) list -> 'a list -> 'b list. At this point, you already run into the problem that pure is a reserved keyword in F#, so you'll have to find another name, or simply ignore that function.

If you need option to be applicative, <*> should have the type ('a -> 'b) option -> 'a option -> 'b option. Now you run into your second problem, because which function is <*>? The one for list, or the one for option? It can't be both, so you'll have to resort to all sorts of hygiene to prevent these two versions of the same operator from clashing. This somewhat limits its usefulness.

Again, refer to the above list of linked articles for concrete examples.

A C# perspective #

Applicative functors push the limits of what you can express in C#, but the equivalent to <*> would be a method with this signature:

public static Functor<TResult> Apply<TTResult>(
    this Functor<Func<TTResult>> selector,
    Functor<T> source)

Here, the class Functor<T> is a place-holder for a proper functor class. A concrete example could be for IEnumerable<T>:

public static IEnumerable<TResult> Apply<TTResult>(
    this IEnumerable<Func<TTResult>> selectors,
    IEnumerable<T> source)

As you can see, here you somehow have to figure out how to combine a sequence of functions with a sequence of values.

In some of the examples in the above list of linked articles, you'll see how this will stretch the capability of C#.

Summary #

This article only attempts to provide an overview of applicative functors, while examples are given in linked articles. I find it helpful to think of applicative functors as an abstraction that enables you to model arbitrary combinations of objects. This is a core feature in Haskell, occasionally useful in F#, and somewhat alien to C#.

Next: Full deck.



Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or somewhere else with a permalink. Ping me with the link, and I may respond.

Published

Monday, 01 October 2018 06:34:00 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!