Note. translator: This is a translation of the first articles from a whole series of posts “Groaming Functional Programming” by Matt Tronton. Yes, this is another article about monads. But it’s different from anything I’ve read on the subject before. Therefore, I wanted to translate it in order to carefully read it myself, to share it with those who find it difficult to perceive such material in English or who did not catch their eye, in order, in the end, to simply save it, since the network is unreliable.

The most common way to explain the monad is to go through category theory. Knowing that a monad is a monoid in the category of endofunctors is both exciting and useful for general development, but of little help in a practical sense. The second, equally popular technique is to resort to the help of images, and now we are already adding the values ​​​​in boxes and getting them out of there (or, in general, a nightmare, rolling on the railroad). I do not argue that images are a good way to look at a phenomenon, but here we have equally moved away from category theory and practice. Comments under such articles do not cease to be filled with questions: what does this give us, did we live without a monad somehow?

The author, like a lecturer in mathematics, who lost the proof of the theorem and proved it right at the blackboard, focusing on an intuitive idea of ​​how one would like to solve the problem, gradually makes his way from a clumsy piece of code to a concise one.

Even if the monad did not exist, we would have to invent it, because it allows us to write cleaner code.

In this post, we will try to understand what is `Монада` personally reinventing it on a working example.

## Small example in F#

We will use the language `F#`, but even if you have not used it before, it will not be difficult for you to figure it out. All you need is to learn the following minimum:

• F# has a type `option`. It represents either the presence of some value (`Some`), or its absence through the value `None`. This type is usually used instead of null to indicate the absence of a value.

• Pattern matching for a type `option` as follows:

``````match anOptionalValue with
| Some x -> // выражение на случай, если значение существует
| None -> // выражение, если значение отсутствует``````
• F# has a pipeline statement that is written like this: `|>` (to be quite precise, this is a direct pipeline operator – `forward pipe operator` approx. translator). It is an infix operator, meaning it applies the value on its left to the function on its right. For example, if the function `toLower` takes a string and converts it to lowercase, then the expression `"ABC" |> toLower` will return `"abc"`.

## Test Scenario

Let’s say we’re writing code that needs to deduct money from a user’s credit card. If the user exists and has a credit card saved in their profile, we can charge the funds, otherwise we will have to signal that nothing happened.

Our data model is `F#`

``````type CreditCard =
{ Number: string
Expiry: string
Cvv: string }

type User =
{ Id: UserId
CreditCard: CreditCard option }``````

Please note that the field `CreditCard` type `User` noted as `option`because the card may not be specified.

## Our first implementation

Let’s try to implement the function `chargeUserCard`. First, we’ll define a couple of helper functions that we’ll use as stubs for finding a user and debiting funds.

``````let chargeCard (amount: double) (card: CreditCard): TransactionId option =
// синхронно списывает средства с карты и возвращает
// некий Id транзакции в случае успеха, иначе возвращает None

let lookupUser (userId: UserId): User option =
// синхронно ищет пользователя, которого может и не быть

let chargeUserCard (amount: double) (userId: UserId): TransactionId option =
let user = lookupUser userId
match user with
| Some u ->
match u.CreditCard with
| Some cc -> chargeCard amount cc
| None -> None
| None -> None``````

Ready! But it turned out pretty messy. Double pattern matching is not the clearest code to read. Yes, in this simple example, we could leave it like that, but what if we had a third or fourth level of nesting?

We could solve this with a few feature extractions, but there is another problem. Note that in both cases, when matching against the value `None` returns `None`. In our example, this doesn’t look scary because the default value is simple and only repeats twice. But we should definitely be capable of better than that.

What we really want is to be able to say “If at some point we can’t continue because some data is missing, then stop and return None.”

## Our desired realization

Let’s imagine for a moment that the data is always present and we don’t have values ​​like `option`. Let’s call this function `chargeUserCardSafe` and it should look something like this:

``````let chargeUserCardSafe (amount: double) (userId: UserId): TransactionId =
let user = lookupUser userId
let creditCard = u.CreditCard
chargeCard amount creditCard``````

Note that the function now returns just `TransactionId` instead of `option`because it cannot fail.

It would be great if we could write code that looks exactly the same, even though the data may sometimes be missing. For this to work, we must put something between these lines of code so that the types match and connect them in this way.

## Refactoring for a cleaner implementation

How is this connecting element supposed to work? It should complete the calculation if in the previous step we got `None`otherwise it must extract the value from `Some` and pass it to the next line. In essence, this is the pattern matching we wrote about above.

Well, let’s see if we can extract it. First, let’s rewrite our function, which always executes correctly, as a pipeline so that we can easily inject our new function between computation steps later.

``````// Эта вспомогательная функция нужна лишь для того,
// чтобы нам было проще объединить все шаги
let getCreditCard (user: User): CreditCard option =
u.CreditCard

let chargeUserCardSafe (amount: double) (userId: UserId): TransactionId =
userId
|> lookupUser
|> getCreditCard
|> chargeCard amount``````

All we have done is turn our calculation into a pipeline with a special operator. Now if the functions `lookupUser` and `chargeCard` will return again `option`, this example will no longer compile. The problem is that we cannot write

``userId |> lookupUser |> getCreditCard``

because `lookupUser` returns type `User option`and we are trying to pass this result to the input of a function that takes `User`.

So we have two ways to fix this error.

1. Write a function like `User option -> User`which will expand the value `option`so that it can be passed down the pipeline. However, ignoring the value `None`, we lose information about the possible lack of data. In imperative programming, this is solved by throwing an exception. But, functional programming is supposed to keep us safe, so we won’t do that.

2. Instead, we can change the function on the right side of the operator so that it can take the type `User option`not just `User`. We need something that takes a function as input and converts it to another function.

We know that this high order function must be of type

``(User -> CreditCard option) -> (User option -> CreditCard option)``

Let’s write it simply respecting the types. We’ll call her `liftGetCreditCard`because it “raises” the function `getCreditCard` to work with input data like `option`.

``````let liftGetCreditCard getCreditCard (user: User option): CreditCard option =
match user with
| Some u -> u |> getCreditCard
| None -> None``````

Excellent! We are approaching our ideal function `chargeUserCard`. Now our code looks like this

``````let chargeUserCard (amount: double) (userId: UserId): TransactionId option =
userId
|> lookupUser
|> liftGetCreditCard getCreditCard
|> chargeCard double``````

Partially applying `getCreditCard` to `liftGetCreditCard`we have created a function with the signature `User option -> CreditCard option`which is what we wanted to achieve.

Actually not really. Now we have the same problem, only further down the call chain. Function `chargeCard` accepts `CreditCard`and we are trying to convey to her `CreditCard option`. No problem, just apply the same trick again.

``````let liftGetCreditCard getCreditCard (user: User option): CreditCard option =
match user with
| Some u -> u |> getCreditCard
| None -> None

let liftChargeCard chargeCard (card: CreditCard option): TransactionId option =
match card with
| Some cc -> cc |> chargeCard
| None -> None

let chargeUserCard (amount: double) (userId: UserId): TransactionId option =
userId
|> lookupUser
|> liftGetCreditCard getCreditCard
|> liftChargeCard (chargeCard amount)``````

## On the threshold of discovery

Have you noticed how similar these two functions are? `lift...`, how are they not very dependent on the type of the first argument? Basically, it’s just a function of the value contained inside `option`to another `option` value. Let’s see if we can write a single function that handles all options. We can do this by renaming the first argument to `f` (for a function) and removing most of the type hints because F# will infer the generic types for us.

``````let lift f x =
match x with
| Some y -> y |> f
| None -> None``````

The type that F# inferred for the function `lift` has the form

``('a -> 'b option) -> ('a option -> 'b option)``

where `'a` and `'b` – generic types. It may seem that this signature is rather verbose and abstract, but let’s put it next to the more specific signature of our function. `liftGetCreditCard`.

``````(User -> CreditCard option) -> (User option -> CreditCard option)

('a -> 'b option) -> ('a option -> 'b option`)``````

specific type `User` has been replaced by a generic `'a`and the concrete type `CreditCard` per type `'b`. This happened because the functions `lift` no matter what is inside the container `option`, it just says “give me some function ‘f’ and I’ll apply it to the value contained in ‘x’ if that value exists.” The only limitation is that the function `f` accepts the type that is inside `option`.

Okay, now we can refactor our function a little more. `chargeUserCard`.

``````let chargeUserCard (amount: double) (userId: UserId): TransactionId option =
userId
|> lookupUser
|> lift getCreditCard
|> lift (chargeCard amount)``````

Now it really doesn’t look like the typeless version `option`. However, let’s do the final touch and rename `lift`in `andThen`, because intuitively, we can think of this function as a continuation of the calculation in the presence of data. Thus, we can say, “Do something, and then, if possible, do something else.”

Our code is quite easy to read and reflects well how we think about solving a similar problem. We look up the user, then if it exists, we get their credit card information, and finally, if it exists, we charge the card.

## Congratulations! You have just discovered the Monad

Function `lift` / `andThen`which we wrote, is what makes the values `option` Monad. Usually, when talking about Monads, such a function is called binding (bind), but this is not so important for understanding monads. What matters is that you can see why we wrote this function and how it works. Essentially a Monad is a class of things with a certain “then-able” functionality. (I did not pick up a similar short term in Russian, the author means that a certain continuation operation can be performed on the monad, which takes into account the result of the previous step. Cleverly, a monad is an abstraction of a linear chain of related computations. approx. translator)

## Hey, I recognize you!

There is another reason why I renamed `lift` in `andThen`. If you develop in JavaScript, everything we have done may seem like `Promise` with method `then`. In this case, you’ve probably already dealt with Monads. `Promise` this is also a monadin fact, I, unlike the author, belong to the camp of those who do not consider Promise to be a monad. Homework – google why. approx. translator). Exactly the same as with `option`it has a method `then`which takes another function as input and calls it on the result of the instance `Promise`if it completed successfully.

## Monads are just “then-able” containers

Another good way to intuitively understand Monads is to think of them as value containers.nevertheless, here the author could not resist the containers approx. translator). `option` it is a container that either contains a value or is empty. `Promise` it is a container that “promises” to store the value of some asynchronous computation if it succeeds.

Of course, there are other Monads, such as `List,` which contains the values ​​of many calculations, and `Result`A that contains the value if the calculation succeeded or an error if it didn’t. For each of these containers, we can define a function `andThen`which defines how to apply a function that takes an object inside the container to the object wrapped in the container.

We learned that Monads are just container types with a “then-able” function, usually called `bind`. We can use this function to chain calculations that take simple values ​​as input and return wrapped values ​​of a different type.

Monads are useful because they are applicable to a large number of tasks. There are many types where we can get rid of boilerplate code by defining a method like this `bind`. A monad is just a name given to such a type, and as Richard Feynman said, names do not constitute knowledge.

## In the next series

If you remember, the original goal that we set for ourselves when we started our refactoring was to write code like this

``````let chargeUserCard (amount: double) (userId: UserId): TransactionId option =
let user = lookupUser userId
let creditCard = u.CreditCard
chargeCard amount creditCard``````

But we still have to deal with the meanings `option`. That is, we have not reached our goal completely. In the next post, we’ll see how computation expressions can be used in F# to achieve a more imperative style even when working with Monads.