Create your own container

This tutorial will guide you through the process of creating your own containers.

Step 0: Motivation

First things first, why would anyone want to create a custom containers?

The great idea about “containers” in functional programming is that it can be literally anything. There are endless use-cases.

You can create your own primitives for working with some language-or-framework specific problem, or just model your business domain.

You can copy ideas from other languages or just compose existing containers for better usability (like IOResult is the composition of IO and Result).


We are going to implement a Pair container for this example. What is a Pair? Well, it is literally a pair of two values. No more, no less. Similar to a Tuple[FirstType, SecondType]. But with extra goodies.

Step 1: Choosing right interfaces

After you came up with the idea, you will need to make a decision: what capabilities my container must have?

Basically, you should decide what Interfaces you will subtype and what methods and laws will be present in your type. You can create just a returns.interfaces.mappable.MappableN or choose a full featured returns.interfaces.container.ContainerN.

You can also choose some specific interfaces to use, like returns.interfaces.specific.result.ResultLikeN or any other.

Summing up, decide what laws and methods you need to solve your problem. And then subtype the interfaces that provide these methods and laws.


What interfaces a Pair type needs?

Now, after we know about all interfaces we would need, let’s find pre-defined aliases we can reuse.

Turns out, there are some of them!

Let’s look at the resul:


A special note on returns.primitives.container.BaseContainer. It is a very useful class with lots of pre-defined feaatures, like: immutability, better cloning, serialization, and comparison.

You can skip it if you wish, but it is highlighly recommended.

Later we will talk about an actual implementation of all required methods.

Step 2: Defining new interfaces and associated laws

After the initial analysis in the “Step 1”, you can decide to introduce your own methods.

These methods can probably form an interface, if you want to make generic utilities for your type.

Let’s say your type will have .from_tuple and .replace methods, that can look like so:

Step 3: Actual implementation

Step 4: Writting tests and docs

Step 5: Writting type-tests

Step 6: Checking laws

Step 7: Reusing code

Context Pipelines