In this article I show how to start with functional programming in TypeScript. At the end of this article, you should know how to do a basic implemention of map and flatMap in Typescript.
Functional programming is not common in TS. Normally, you would use languages like Haskell and Scala to program functionally. However, TypeScript supports types and function arguments. This means that you can use it to do FP as well! Let’s start with the basics of functional programming.
What is a Monad?
The core building block of functional programming is a monad. “What is a monad?” is a common question you hear a lot when starting with functional programming. In short,
“A monad is a monoid in the category of endofunctors”
see wiki.
A monoid has two properties, an identity operation and associativity. These properties define how elements from a set can be combined and what result that produces. For example, the + is a monoid in the space of numbers. With +
, you can add 0
and still keep the same number (identity). Also, you can change the order of the operations and keep the same equation (3+4)+5=3+(4+5)=12
(associativity). Monads work the same, expect they are defined in a more abstract way.
Implementing a Monad in TypeScript
In programming, you can use monads to define transformations on data types. In TypeScript, this is the interface definition of a Monad for a value of type T:
1 2 3 4 5 6 7 |
interface Monad<T> { map<B>(transform: (value: T) => P): Monad<P>; flatMap<B>(transform: (value: T) => Monad<P>): Monad<P>; } |
This is a basic example on how to implement this interface in a class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
export class MyMonad<T> implements Monad<T> { private readonly value: T; constructor(value: T) { this.value = value; } map<P>(f: (x: T) => P) { return new MyMonad(f(this.value)); } flatMap<P>(f: (x: T) => MyMonad<P>) { return f(this.value); } } |
As you can see, you can functionally access the value in the class and transform it to another monad using map
. With flatMap, you transform the value into another monad. FlatMap makes sure that when you combine monads, the value in the class remains of type P
and does not become a value of type Monad<P>
.
Using Map in TypeScript
One of the advantages of using monads in TypeScript, is that the code becomes more readable. We can use the simple monad definition above to illustrate this. Let’s say I have the following two transformations.
1 2 3 4 5 6 7 8 9 |
function transformation1(x: T): P { // transform x.. return x + 1; } function transformation2(y: P): Y { // transform y.. return y * 3; } |
My program applies a list of functions to transform data.
1 2 3 4 |
// Program function main() { return transformation1(transformation2(3)); } |
With the monad class from above, I can rewrite the same program like this:
1 2 3 4 5 6 |
// Program function main() { return (new Monad(3)) .map(transformation1) .map(transformation2); } |
The advantage is that instead of function composition, I can just use map to list all the transformation in the program.
Using FlatMap in Typescript
With flatMap, you can combine programs that use monads. Let’s do an example program that writes a csv file from some input data and returns whether is was succesfull or not.
1 2 3 4 5 6 7 8 9 10 11 12 |
function writeCsv(x: number): MyMonad<“Success” | “Failure”> { try { // write csv … return new MyMonad(“Success”); } catch (e) { return new MyMonad<“Failure”> } } |
Now I can combine this program with the program from the previous example. The end result is that it writes the transformed value to csv.
1 2 3 4 5 6 7 |
// Combined program function main() { return (new Monad(3)) .map(transformation1) .map(transformation2) .flatMap(x => writeCsv(x)); } |
The program also now keeps track of if there were any error codes.
Next steps
In this article, I showed how to implement and use a basic monad with TypeScript. Also, I showed a few basic examples of using monads.
However, this is just the start of the things you can do with functional programming and monads!
For my next article, I will implement a common utility class, the promise
, using monads.