Functional Programming Option
type is your best friend against null
and null-related errors (Have you ever heard of NullPointerException
?).
Option
represents some value, usingSome
, or the absence of it, usingNone
In this article, I am going to introduce you to the Option
type: what is it, why would you want to use it, and how it works. The examples are written in Dart using the fpdart package, Functional Programming in dart and Flutter.
Option
type, why not using null
instead?
The problem with null
is that it can be assigned to any type.
When you assign a type to a variable, for example String
, it's because you want to help yourself by saying: "This value is a String
, and, from now on, I can use it and access methods only where a String
is accepted!".
This contract makes your life as a developer much easier; you set a limit for yourself that the compiler will ensure you will not be able to cross.
But, guess what, null
is the exception! null
is the only value that is not a String
but it is still allowed to enter places where only a String
is allowed to enter.
This breaks a huge assumption in your code. Wherever you expect String
and only String
, you may find yourself dealing with a null
instead! What happens if you try to access a method that only String
has from a null
? Yes, NullPointerException
!
Option
type to the rescue
Option
makes explicit the fact that a value may be missing. Moreover, it allows to update and chain methods as if the value was actually present.
Practically speaking, Option
itself can assume two values:
Some
, a type that contains the value of theOption
when it is presentNone
, a type that tells us that theOption
does not contain any value
How to initialize an Option
The Option
type can be initialized directly using Some
and None
.
const some = Some(10);
const none = None<int>();
Option
can also be initialized using specific constructors that automatically build an instance of Option
from different functions:
// --- Initialize an Option 👇 --- //
const someInit = Some(10);
const noneInit = None<int>();
final someInit2 = some(10);
final noneInit2 = none<int>();
/// Create an instance of [Some]
final option = Option.of(10);
/// Create an instance of [None]
final noneInit3 = Option<int>.none();
/// If the predicate is `true`, then [Some], otherwise [None]
final predicate = Option<int>.fromPredicate(10, (a) => a > 5);
/// If no exception, then [Some], otherwise [None]
final tryCatch = Option<int>.tryCatch(() => int.parse('10'));
/// When the value is not `null`, then [Some], otherwise [None]
final nullable = Option<int>.fromNullable(10);
Option
type example
Let's say for example that you have a catalog of products in your application.
Your function takes the name of the product and returns its price.
In our example, the price of the product is simply its name's length. Nonetheless, we know that our catalog does not accept products with names longer than 6 characters.
For example, the price of 'milk' is 4, while the price of chocolate is not defined.
If we use null
to encode a product that does not have a price, our function will look like this:
int? getPrice(String productName) {
if (productName.length > 6) {
return null;
}
return productName.length;
}
void main() {
final price = getPrice('my product name');
if (price == null) {
print('Sorry, no product found!');
} else {
print('Total price is: $price');
}
}
We need to use an if-else statement to handle the case when the value is missing.
If we use Option
instead the function will be the following:
import 'package:fpdart/fpdart.dart';
Option<int> getPrice(String productName) {
if (productName.length > 6) {
return none();
}
return some(productName.length);
}
void main() {
final price = getPrice('my product name');
price.match(
(a) {
print('Total price is: $price');
},
() {
print('Sorry, no product found!');
},
);
}
The Option
type has a match
function that allows us to handle both when the value is present and when the value is missing.
Recap: What we learned in this article
- Why using
Option
instead ofnull
:Option
allows us to explicitly handle missing values. - How to initialize an
Option
type: There are many different constructors that build an instance ofOption
from a normal value of any type. match
method: Pattern matching on anOption
value to handle both the presence and absence of the value inside theOption
.
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 👇