Handling option(’a) values in ReasonML with Belt.Option

Gabriel Rubens Abreu
Astrocoders
Published in
3 min readDec 8, 2018

--

The article presumes you are familiar a bit into ReasonML and heard about Belt. If you have any other questions leave them in the comments below!

If you are not aware about, the `option(‘a)` type is for handling values that are possibly not there. It’s like JS `null` and `undefined` but way better.

Transforming optionals

Most of the time we are handling optionals from reason-apollo responses so we’ll be dealing with JS types in ReasonML instead of records. Here, consider the type:

type user = {
.
"id": string,
"products": option(array({ . "name": string }))
};
type result = {
.
"data": option({ . "currentUser": option(user) })
"error": option(string),
};

How would we extract the list of products from this? This first approach a beginner may try is to add lots of pattern-matching:

let response = ...;
let products =
switch (response##data) {
| Some(data) =>
switch (data##currentUser) {
| Some(user) =>
switch (user##products) {
| Some(products) => products
| None => [||]
}
| None => [||]
}
| None => [||]
};

Pattern matching is amazing to avoid us doing and mishandling possible cases as it warns if we forgot to handle any branch of the solution. Though, in this very case it forces us to add a lot of code just to get the `products` out of the response and this does not scalate well.

With Belt.Option utils

If you are familiar a bit with FP you may have realized that option(‘a) is monad-y, so we can apply monad operations on it. Including map and flatMap. And that’s some of the methods Belt.Option gives us, check out the documentation. Let’s rewrite the code above with Belt”

let response = ...;
let products =
response##data
->Belt.Option.flatMap(data => data##currentUser)
->Belt.Option.flatMap(user => user##products)
->Belt.Option.getWithDefault([||]);

That’s way better!
We can even do a local open for Belt.Option to avoid repetition:

let response = ...;
let products =
Belt.Option.(
response##data
->flatMap(data => data##currentUser)
->flatMap(user => user##products)
->getWithDefault([||]),
);

And that’s it for today :)

Links I recommend you reading if you are new to this:

Gabriel Rubens Abreu, Head of Research & Development at Astrocoders.com. Follow me on twitter.com/_gabrielrubens.

--

--