The term “monad” originates from category theory and is often labeled as the most challenging branch of Mathematics. We’ll look at what a monad is before diving deep into its usability in functional programming.
A monad can be thought of as a functor that wraps another functor and provides some form of quality to the underlying type in functional programming. Monads define two functions in addition to the wrapper type. They wrap a value in a monad and also assemble functions that output the monads.
Three components make up a monad:
Wrapper type
Wrap function
Run function
It might seem confusing, but things will start to make sense when we look at how monads work in TypeScript.
We’ll create two functions. The first will provide us with the square of the given number while the second function returns the number with 2
added to it.
function squared(a: number): number {return a * a}function addTwo(a:number): number {return a + 2}console.log(addTwo(squared(2)))
Everything works as intended, and we get 6
as the output. However, what if we wanted to see step-by-step working of our function call?
Sometimes we want to see the inner working of our program to ensure that the code is following our designed logic and doesn’t return the desired output by doing something we didn’t anticipate.
Therefore, we can modify our functions a little to help us:
interface numberswithinfo {calnum: numberinfo: string[]}function squared(a: number): numberswithinfo {return{calnum: a * a,info: [`We squared ${a} to retrieve ${a * a}`]}}function addTwo(a:numberswithinfo): numberswithinfo {return{calnum: a.calnum + 2,info: a.info.concat([`We added 2 to ${a.calnum} to retrieve ${a.calnum + 2}`])}}console.log(addTwo(squared(2)))
Great! We are getting some information about our functions too. However, things will break apart if we run the following command:
console.log(squared(squared(5)))
The error is displayed because our squared function only takes a number input, but we provided it with info too.
To solve this problem, we’ll create a monad that will wrap things up for us and execute them without giving any type errors:
/** Wrapping Function */function wrapperwithinfo(a:number): numberswithinfo {return {calnum: a,info: []}}/** Run Function */function runwithinfo(input: numberswithinfo,transform: (_: number) => numberswithinfo): numberswithinfo {const newnumberswithinfo = transform(input.calnum)return {calnum:newnumberswithinfo.calnum,info: input.info.concat(newnumberswithinfo.info)}}/** Test Command */const x = runwithinfo(wrapperwithinfo(2), squared)console.log(runwithinfo(x, squared))
Congratulations! You’ve finally learned what monads are and how to use them in your TypeScript code.
Free Resources