In an earlier blog, we discussed Functional Programming Essentials for crafting clean and effective code. This blog will discuss more advanced topics related to functional programming, diving deeper into the funky land of functional programming.
Functors
In Functional Programming, a Functor is simply something that can be mapped over. In Haskell, a Functor is an in-built class with a function definition like:
Functors allow a uniform action over a parameterized type. We can define Functor in TypeScript like this:
Here, we define a Functor interface, which has map method. The map method takes a function fn that maps the value inside the functor from type A to type B. But, for any interface or container to be a functor, the map must obey two laws:
Identity: Applying map() on the identity function must return the same container without any changes.
Composition:F.map(x => f(g(x))) is equivalent to F.map(g).map(f).
Functors play an important role in functional programming by providing a way to apply functions to values in a composable and structured manner.
Functors can be composed together to build more complex data transformations.
Monads
A monad is a generic structure in functional programming that handles side effects in pure functions.
In my blog, Functional Programming Essentials, we discovered about importance of writing pure functions with no side effects. But in practice, applications need to have some side effects.
Simon Peyton-Jones, a major contributor to the functional programming language Haskell, said the following: "In the end, any program must manipulate state. A program that has no side effects whatsoever is a kind of black box. All you can tell is that the box gets hotter."
The key is to limit side effects, clearly identify them and avoid scattering them throughout our code. Monads help to manage computations in a purely functional manner. We can define Monad in Typescript like this:
The file Monad Class helps us work with files more composably. It also helps us deal with errors more composedly within the chain method, ensuring that our errors are properly managed.
Another good example of a monad is the Maybe Monad, whose definition looks like this in Haskell:
It represents the possible non-existence of a value. Imagine a scenario where we need to fetch some data from the database, but the data can be null.
Although this style of coding is not wrong, we would need to check for null values every time.
A good way to handle this would be to use a maybe monad:
Now, We can use our maybe Monad like this:
The maybe Monad can help us properly handle null values without using a bunch of if and else.
Conclusion
To wrap up, we learnt about Functor and Monads and how to use them in Typescript to write "functional" code. You can look at libraries like fp-ts and ts-pattern to write functional Typescript code.