# Monadic operations in C++23

If you are a C++ developer, and you have not been living under a rock, you certainly know that C++23 is finalized! One of my favourite addition is monadic operations.

Before diving into the details, let’s take a step back and try to define a monad. I will give a very simple (maybe incomplete) definition, that comes from a software developer perspective. For the purpose of this discussion a monad M that accepts a type T provides a set of operations:

- A constructor or factory function that accepts any type
`T`

and “lifts” it to the monadic type. - An operation
`transform`

that takes as input a monad`M<T>`

and a function1

S f(T t);

and returns a

`M<S>`

, with`S`

being the result of`f(t)`

, if`M<T>`

exists, or a default monadic value if`M<T>`

does not exist. - An operation
`and_then`

that takes as as input a monad`M<T>`

and a function1

M<S> f(T t);

and returns a

`M<S>`

, with`M<S>`

being the result of`f(t)`

, if`M<T>`

exists, or a default monadic value if`M<T>`

does not exist. - An operation
`or_else`

that takes as as input a monad`M<T>`

and a function, and returns`M<T>`

if it exists or executes the function.

If you are wondering what is the difference between `transform`

and `and_then`

I got you covered! Simply put, if you where to apply `transform`

to a function

1

M<S> f(T t);

you could get the output `M<M<S>>`

so `and_then`

simply flattens the result of applying `f`

to `t`

.

By this definition of monad in C++23 `optional`

became a monadic type! To understand why we care let’s look at a simple example.

Let’s say I want to compute the square root of a number, later I will add 42 to this number. Using c++17 `optional`

an implementation might look like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

#include <cmath>
#include <optional>
#include <iostream>
std::optional<float> my_sqrt(float x){
return x > 0 ? std::make_optional(sqrtf(x)) : std::nullopt;
}
float my_add_42(float x){
return x + 42;
}
int main(){
const std::optional<float> x = 144;
std::optional<float> out = std::nullopt; /* (1) */
auto sqrt_x = my_sqrt(*x);
if (sqrt_x.has_value()){ /* (2) */
out = my_add_42(*sqrt_x);
}
std::cout << out.value_or(-1) << std::endl;
}

In the snippet, if x is a negative numer or `std::nullopt`

, I will get `-1`

, otherwise the value of the operation will be displayed. We deal with the condition that `my_sqrt`

could return an empty optional by checking if the result has a value (2). This is not ideal because breaks the logic flow. Also I am forced to first initialize the `out`

variable (1) and then assign a value to it. Using the power of monadic operations in c++23 we can craft a cleaner solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

#include <cmath>
#include <optional>
#include <iostream>
std::optional<float> my_sqrt(float x){
return x > 0 ? std::make_optional(sqrtf(x)) : std::nullopt;
}
float my_add_42(float x){
return x + 42;
}
int main(){
const std::optional<float> x = 44;
const std::optional<float> out =
x.and_then(my_sqrt)
.transform(my_add_42)
.or_else( [](){return std::make_optional<float>(-1); });
std::cout << *out << std::endl;
}

This code is functionally equivalent to the previous snippet but it has three main advantages:

- Reduced use of if statements.
- output variable is assigned in only one statement, so it can be const.
- Easy chaining of operations leading to fluent API.

`optional`

is not the only type to implement monadic operations. A similar interface is implemented by `expected`

, that I will describe in a future post.