List monad


Martin McBride, 2020-07-02
Tags monad list monad design pattern
Categories functional programming
In Programming techniques

In the article on the Failure monad we looked at a simple definition of what a monad is:

A monad is a design pattern that allows us to add a context to data values, and also allows us to easily compose existing functions so that they execute in a context aware manner.

In this article we will look at the List monad. This is a monad that allows us to process lists of values, without cluttering our code with looping constructs.

How a List monad is used

As with the Failure monad, our List monad should do three main things:

  • Allow us to wrap a value (in this case, a list value) in a List monad.
  • Allow us to apply ant existing function to the entire list, using bind().
  • Allow us to retrieve the List from a monad.

So a simple use might be like this:

k = List([1, 2, 3])
n = k.bind(neg)
print(n)              # List([-1, -2, -3])

We will call our monad List because that is the name that is commonly used for similar monads in other languages. Notice that a List monad is quite different to a normal Python list.

List monad implementation

Based on the previous implementation of the Failure monad, here is our List monad:

class List():

    def __init__(self, value):
        self.value = value

    def get(self):
        return self.value

    def bind(self, f):
        result = list(map(f, self.value))
        return List(result)

    def __str__(self):
        return 'List(' + ', '.join(map(str, self.value)) + ')'

    def __or__(self, f):
        return self.bind(f)

The List monad just wraps a single value, which should be a list or other sequence. This is stored as self.value.

The bind function uses map to apply the supplied function f to each element in self.value. The resulting list is wrapped in a new List monad.

We override str to return 'List([contents of list])'. Finally we override the or operator | so we can use it as a bind operator.