The most interesting property of Functional Programming is composition. The Option
type allows composing small functions to form a bigger function. Since every function is pure, we can test every single function in isolation.
If all the basic functions are correct, we can be (almost) sure that a bigger and more complex function composed from these building blocks is also correct!
Today you will learn four methods used to compose Option
together:
alt
: Provides an alternativeOption
in case the current one isNone
.andThen
: If the currentOption
contains a value (Some
) return a new givenOption
.flatMap
: Extract the value inside theOption
if it is present and return a newOption
, otherwise returnNone
.getOrElse
: Extract the value if present, or provided a fallback value if it is not present.
I highly encourage you to read the previous article in the series. Many of the basic concepts have been explained in the previous article, so you may have trouble understand this post if you do not read the previous one.
Shopping with Functional Programming and Option
The example of this article is all about shopping:
You want to go shopping. Your first option is going to the Shopping Center. Nonetheless, if the Shopping Center is closed, you will go to the Local Market, which is always open.
You want to buy exactly one Banana (π), one Apple (π), and one Pear (π). If any one of these fruits is missing, you will leave empty handed and not buy anything.
We will model these instructions using Functional Programming with the Option
type!
Shopping Center, otherwise Local Market
Your first idea is to go to the Shopping Center. You define a function for doing this:
Option<Unit> goToShoppingCenter() => getRandomOption(unit);
getRandomOption(value)
This function returns the given
value
randomly wrapped in anOption
:Some(value)
half the times, andNone()
the other half.
The function returns an Option
, since 50% of the times the Shopping Center is closed!
You also define an alternative function to go to the Local Market:
Option<Unit> goToLocalMarket() => some(unit);
This function always returns a Some
, since the Local Market is always open!
Now you want to combine these two functions. You try to go to the Shopping Center. If it is closed, you will go to the Local Market instead:
goToShoppingCenter().alt(
goToLocalMarket,
);
We use the alt
function. If goToShoppingCenter
is Some
, then alt
will just return this Some
, If goToShoppingCenter
is None
instead, alt
will return the result of calling goToLocalMarket
.
And then, once we are in the market, let's buy
Regardless of which market we choose, we want to start buying some fruits!
We don't really care about the Unit
that the two previous functions return. We are just going to ignore it and start buying:
goToShoppingCenter().alt(
goToLocalMarket,
).andThen(() => /** Let's buy! */);
The method andThen
throws away the result of the previous Option
if it is a Some
and just calls the given function. If the previous Option
is None
, then andThen
will also return None
.
Let's but banana AND apple AND pear, nothing otherwise!
Now you are inside the market and ready to buy. You want all the fruit on your shopping list. If any of the fruit is not available, you will just leave and not buy anything.
We define three functions that give us our fruits (only 50% of the time):
Option<String> buyBanana() => getRandomOption('π');
Option<String> buyApple() => getRandomOption('π');
Option<String> buyPear() => getRandomOption('π');
You are going to check the availability of all these three fruits one by one. If, at any step in the process, you don't find the fruit you were looking for, you will just leave all the other fruits taken since then and leave:
goToShoppingCenter()
.alt(
goToLocalMarket,
)
.andThen(
() => buyBanana().flatMap(
(banana) => buyApple().flatMap(
(apple) => buyPear().flatMap(
(pear) => Option.of('Shopping: $banana, $apple, $pear'),
),
),
),
);
We use flatMap
to chain these functions. flatMap
will call the given function only when the Option
is Some
. If any of the Option
in the chain is None
, then the whole chain will return None
!
Get what we bought, or else nothing
Finally, once you are back home you check what you bought. If you did not find your fruits, you will just notify your family. Otherwise you will show the result of your expedition:
// Combine all the instructions and go shopping! π
String goShopping() => goToShoppingCenter()
.alt(
goToLocalMarket,
)
.andThen(
() => buyBanana().flatMap(
(banana) => buyApple().flatMap(
(apple) => buyPear().flatMap(
(pear) => Option.of('Shopping: $banana, $apple, $pear'),
),
),
),
)
.getOrElse(
() => 'I did not find π or π or π, so I did not buy anything π€·ββοΈ',
);
getOrElse
returns the value inside the Option
if the Option
is Some
, Otherwise, it returns the given value in the provided function.
We record the results of our visit to the market for 100 days, and this is the result:
Sometimes you find all the fruits and you buy them, other times you just leave empty-handed.
Recap: What we learned in this article
- How to use the methods
alt
,andThen
,flatMap
to chain functions returningOption
- How to use
getOrElse
to extract the value from anOption
- How to model a Functional Programming application using
Option
You can find the complete example in the fpdart repository:
Do you like these short articles on Functional Programming? Let me know on Twitter at @SandroMaglione. Follow me for daily updates on Functional Programming, dart, Flutter, mobile, and web development. If you are interested in more tips and guides about these topics, subscribe to my newsletter here below π