Options
Introduction
An option value wraps another value or contains nothing if there isn't anything to wrap. The predefined type option
represents such values.
# #show option;;
type 'a option = None | Some of 'a
Here are option values:
# Some 42;;
- : int option = Some 42
# None;;
- : 'a option = None
Here we have:
- 42, stored inside an
option
using theSome
constructor and - the
None
value, which doesn't store anything.
The option type is useful when the lack of data is better handled as the special value None
rather than an exception. It is the type-safe version of returning error values. Since no wrapped data has any special meaning, confusion between regular values and the absence of value is impossible.
Exceptions vs Options
The function Sys.getenv : string -> string
from the OCaml standard library queries the value of an environment variable; however, it throws an exception if the variable is not defined. On the other hand, the function Sys.getenv_opt : string -> string option
does the same, except it returns None
if the variable is not defined. Here is what may happen if we try to access an undefined environment variable:
# Sys.getenv "UNDEFINED_ENVIRONMENT_VARIABLE";;
Exception: Not_found.
# Sys.getenv_opt "UNDEFINED_ENVIRONMENT_VARIABLE";;
- : string option = None
See Error Handling for a longer discussion on error handling using options, exceptions, and other means.
Option
Module
The Standard Library Most of the functions in this section, as well as other useful ones, are provided by the OCaml standard library in the Stdlib.Option
module.
Map Over an Option
Using pattern matching, it is possible to define functions, allowing to work with option values. Here is map
of type ('a -> 'b) -> 'a option -> 'b option
. It allows to apply a function to the wrapped value inside an option
, if present:
let map f = function
| None -> None
| Some v -> Some (f v)
In the standard library, this is Option.map
.
# Option.map (fun x -> x * x) (Some 3);;
- : int option = Some 9
# Option.map (fun x -> x * x) None;;
- : int option : None
Peel-Off Doubly Wrapped Options
Here is join
of type 'a option option -> 'a option
. It peels off one layer from a doubly wrapped option:
let join = function
| Some Some v -> Some v
| Some None -> None
| None -> None
In the standard library, this is Option.join
.
# Option.join (Some (Some 42));;
- : int option = Some 42
# Option.join (Some None);;
- : 'a option = None
# Option.join None;;
- : 'a option = None
Access the Content of an Option
The function get
of type 'a option -> 'a
allows access to the value contained inside an option
.
let get = function
| Some v -> v
| None -> raise (Invalid_argument "option is None")
Beware, get o
throws an exception if o
is None
. To access the content of an option
without the risk of raising an exception, the function value
of type 'a option -> 'a -> 'a
can be used:
let value opt ~default = match opt with
| Some v -> v
| None -> default
However, it needs a default value as an additional parameter.
In the standard library, these functions are Option.get
and Option.value
.
Fold an Option
The function fold
of type none:'a -> some:('b -> 'a) -> 'b option -> 'a
can be seen as a combination of map
and value
let fold ~none ~some o = o |> Option.map some |> Option.value ~default:none
In the standard library, this function is Option.fold
.
The Option.fold
function can be used to implement a fall-back logic without writing pattern matching. For instance, here is a function that turns the contents of the $PATH
environment variable into a list of strings, or the empty list if undefined. This version uses pattern matching:
# let path () =
let split_on_colon = String.split_on_char ':' in
let opt = Sys.getenv_opt "PATH" in
match opt with
| Some s -> split_on_colon s
| None -> [];;
val path : unit -> string list = <fun>
Here is the same function, using Option.fold
:
# let path () =
let split_on_colon = String.split_on_char ':' in
Sys.getenv_opt "PATH" |> Option.fold ~some:split_on_colon ~none:[];;
val path : unit -> string list = <fun>
Bind an Option
The bind
function of type 'a option -> ('a -> 'b option) -> 'b option
works a bit like map
. But whilst map
expects a function parameter f
that returns an unwrapped value of type b
, bind
expects an f
that returns a value already wrapped in an option 'b option
.
Here, we display the type of Option.map
, with parameters flipped and show a possible implementation of Option.bind
.
# Fun.flip Option.map;;
- : 'a option -> ('a -> 'b) -> 'b option = <fun>
# let bind o f = o |> Option.map f |> Option.join;;
val bind : 'a option -> ('a -> 'b option) -> 'b option = <fun>
Observe that the types are the same, except for the codomain of the function parameter.
Conclusion
By the way, any type where map
and join
functions can be implemented, with similar behaviour, can be called a monad, and option
is often used to introduce monads. But don't freak out! You don't need to know what a monad is to use the option
type.
Help Improve Our Documentation
All OCaml docs are open source. See something that's wrong or unclear? Submit a pull request.