The Option data type represents optional values. An Option<A> can either be Some<A>, containing a value of type A, or None, representing the absence of a value.
You can use Option in scenarios like:
Using it for initial values
Returning values from functions that are not defined for all possible inputs (referred to as “partial functions”)
Managing optional fields in data structures
Handling optional function arguments
Creating Options
some
Use the Option.some constructor to create an Option that holds a value of type A.
Example (Creating an Option with a Value)
none
Use the Option.none constructor to create an Option representing the absence of a value.
Example (Creating an Option with No Value)
liftPredicate
You can create an Option based on a predicate, for example, to check if a value is positive.
Example (Using Explicit Option Creation)
Here’s how you can achieve this using Option.none and Option.some:
Example (Using Option.liftPredicate for Conciseness)
Alternatively, you can simplify the above logic with Option.liftPredicate:
Modeling Optional Properties
Consider a User model where the "email" property is optional and can hold a string value. We use the Option<string> type to represent this optional property:
Here are examples of how to create User instances with and without an email:
Example (Creating Users with and without Email)
Guards
You can check whether an Option is a Some or a None using the Option.isSome and Option.isNone guards.
Example (Using Guards to Check Option Values)
Pattern Matching
Use Option.match to handle both cases of an Option by specifying separate callbacks for None and Some.
Example (Pattern Matching with Option)
Working with Option
map
The Option.map function lets you transform the value inside an Option without manually unwrapping and re-wrapping it. If the Option holds a value (Some), the transformation function is applied. If the Option is None, the function is ignored, and the Option remains unchanged.
Example (Mapping a Value in Some)
When dealing with None, the mapping function is not executed, and the Option remains None:
Example (Mapping over None)
flatMap
The Option.flatMap function is similar to Option.map, but it is designed to handle cases where the transformation might return another Option. This allows us to chain computations that depend on whether or not a value is present in an Option.
Consider a User model that includes a nested optional Address, which itself contains an optional street property:
In this model, the address field is an Option<Address>, and the street field within Address is an Option<string>.
We can use Option.flatMap to extract the street property from address:
Example (Extracting a Nested Optional Property)
If user.address is Some, Option.flatMap applies the function (address) => address.street to retrieve the street value.
If user.address is None, the function is not executed, and street remains None.
This approach lets us handle nested optional values concisely, avoiding manual checks and making the code cleaner and easier to read.
filter
The Option.filter function allows you to filter an Option based on a given predicate. If the predicate is not met or if the Option is None, the result will be None.
Example (Filtering an Option Value)
Here’s how you can simplify some code using Option.filter for a more idiomatic approach:
Original Code
Refactored Idiomatic Code
Using Option.filter, we can write the same logic more concisely:
Getting the Value from an Option
To retrieve the value stored inside an Option, you can use several helper functions provided by the Option module. Here’s an overview of the available methods:
getOrThrow
This function extracts the value from a Some. If the Option is None, it throws an error.
Example (Retrieving Value or Throwing an Error)
getOrNull / getOrUndefined
These functions convert a None to either null or undefined, which is useful when working with non-Option-based code.
Example (Converting None to null or undefined)
getOrElse
This function allows you to specify a default value to return when the Option is None.
Example (Providing a Default Value When None)
Fallback
orElse
When a computation returns None, you might want to try an alternative computation that yields an Option. The Option.orElse function is helpful in such cases. It lets you chain multiple computations, moving on to the next if the current one results in None. This approach is often used in retry logic, attempting computations until one succeeds or all possibilities are exhausted.
Example (Attempting Alternative Computations)
firstSomeOf
You can also use Option.firstSomeOf to get the first Some value from an iterable of Option values:
Example (Retrieving the First Some Value)
Interop with Nullable Types
When dealing with the Option data type, you may encounter code that uses undefined or null to represent optional values. The Option module provides several APIs to make interaction with these nullable types straightforward.
fromNullable
Option.fromNullable converts a nullable value (null or undefined) into an Option. If the value is null or undefined, it returns Option.none(). Otherwise, it wraps the value in Option.some().
Example (Creating Option from Nullable Values)
If you need to convert an Option back to a nullable value, there are two helper methods:
Option.getOrNull: Converts None to null.
Option.getOrUndefined: Converts None to undefined.
Interop with Effect
The Option type works as a subtype of the Effect type, allowing you to use it with functions from the Effect module. While these functions are built to handle Effect values, they can also manage Option values correctly.
How Option Maps to Effect
Option Variant
Mapped to Effect
Description
None
Effect<never, NoSuchElementException>
Represents the absence of a value
Some<A>
Effect<A>
Represents the presence of a value
Example (Combining Option with Effect)
Combining Two or More Options
zipWith
The Option.zipWith function lets you combine two Option values using a provided function. It creates a new Option that holds the combined value of both original Option values.
Example (Combining Two Options into an Object)
If either of the Option values is None, the result will be None:
Example (Handling None Values)
all
To combine multiple Option values without transforming their contents, you can use Option.all. This function returns an Option with a structure matching the input:
If you pass a tuple, the result will be a tuple of the same length.
If you pass a struct, the result will be a struct with the same keys.
If you pass an Iterable, the result will be an array.
Example (Combining Multiple Options into a Tuple and Struct)
If any of the Option values are None, the result will be None:
Example
gen
Similar to Effect.gen, Option.gen provides a more readable, generator-based syntax for working with Option values, making code that involves Option easier to write and understand. This approach is similar to using async/await but tailored for Option.
Example (Using Option.gen to Create a Combined Value)
When any of the Option values in the sequence is None, the generator immediately returns the None value, skipping further operations:
Example (Handling a None Value with Option.gen)
In this example, Option.gen halts execution as soon as it encounters the None value, effectively propagating the missing value without performing further operations.
The use of console.log in these example is for demonstration purposes only. When using Option.gen, avoid including side effects in your generator functions, as Option should remain a pure data structure.
Equivalence
You can compare Option values using the Option.getEquivalence function. This function allows you to specify how to compare the contents of Option types by providing an Equivalence for the type of value they may contain.
Example (Comparing Optional Numbers for Equivalence)
Suppose you have optional numbers and want to check if they are equivalent. Here’s how you can use Option.getEquivalence:
Sorting
You can sort a collection of Option values using the Option.getOrder function. This function helps specify a custom sorting rule for the type of value contained within the Option.
Example (Sorting Optional Numbers)
Suppose you have a list of optional numbers and want to sort them in ascending order, with empty values (Option.none()) treated as the lowest:
Example (Sorting Optional Dates in Reverse Order)
Consider a more complex case where you have a list of objects containing optional dates, and you want to sort them in descending order, with Option.none() values at the end: