In programming languages (especially functional programming languages) and type theory, an option type or maybe type is a polymorphic type that represents encapsulation of an optional value; e.g., it is used as the return type of functions which may or may not return a meaningful value when they are applied. It consists of a constructor which either is empty (often named None or Nothing), or which encapsulates the original data type A (often written Just A or Some A).
A distinct, but related concept outside of functional programming, which is popular in object-oriented programming, is called nullable types (often expressed as A?). The core difference between option types and nullable types is that option types support nesting (e.g. Maybe (Maybe String) ≠ Maybe String), while nullable types do not (e.g. String?? = String?).
Theoretical aspects In type theory, it may be written as: . This expresses the fact that for a given set of values in , an option type adds exactly one additional value (the empty value) to the set of valid values for . This is reflected in programming by the fact that in languages having tagged unions, option types can be expressed as the tagged union of the encapsulated type plus a unit type. An option type is a particular case of a tagged union, where the Nothing is taken as (nullary constructor for a) singleton type. Tagged unions can generally be implemented by a combination of union types and record types using occurrence typing.https://arxiv.org/pdf/2111.03354 p. 8
The option type is also a monad where:
return = Just -- Wraps the value into a maybe
Nothing >>= f = Nothing -- Fails if the previous monad fails (Just x) >>= f = f x -- Succeeds when both monads succeed
The monadic nature of the option type is useful for efficiently tracking failure and errors.
Examples
Ada Ada does not implement option-types directly, however it provides discriminated types which can be used to parameterize a record. To implement an Option type, a Boolean type is used as the discriminant; the following example provides a generic to create an option type from any non-limited constrained type: generic -- Any constrained & non-limited type. type Element_Type is private; package Optional_Type is -- When the discriminant, Has_Element, is true there is an element field, -- when it is false, there are no fields (hence the null keyword). type Optional (Has_Element : Boolean) is record case Has_Element is when False => Null; when True => Element : Element_Type; end case; end record; end Optional_Type;
Example usage: package Optional_Integers is new Optional_Type (Element_Type => Integer); Foo : Optional_Integers.Optional := (Has_Element => True, Element => 5); Bar : Optional_Integers.Optional := (Has_Element => False);
Agda
In Agda, the option type is named with variants and .
ATS
In ATS, the option type is defined as
datatype option_t0ype_bool_type (a: t@ype+, bool) = | Some(a, true) of a | None(a, false) stadef option = option_t0ype_bool_type typedef Option(a: t@ype) = [b:bool] option(a, b)
#include "share/atspre_staload.hats"
fn show_value (opt: Option int): string = case+ opt of | None() => "No value" | Some(s) => tostring_int s
implement main0 (): void = let val full = Some 42 and empty = None in println!("show_value full → ", show_value full); println!("show_value empty → ", show_value empty); end
show_value full → 42 show_value empty → No value
C++ Since C++17, the option type is defined in the standard library as . import std;
using std::nullopt; using std::optional;
constexpr optional<double> divide(double x, double y) noexcept { if (y != 0.0) { return x / y;
return nullopt; }
void readDivisionResults(int x, int y) { optional<double> result = divide(x, y); if (result) { std::println("The quotient of x: {} and y: {} is {}.", x, y, result.value()); } else { std::println("The quotient of x: {} and y: {} is undefined!", x, y);
int main(int argc, char* argv[]) { readDivisionResults(1, 5); readDivisionResults(8, 0);
In C++23, support for monadic operations for is available.
Elm
In Elm, the option type is defined as .
F#
In F#, the option type is defined as .
let showValue = Option.fold (fun _ x -> sprintf "The value is: %d" x) "No value"
let full = Some 42 let empty = None
showValue full |> printfn "showValue full -> %s" showValue empty |> printfn "showValue empty -> %s"
showValue full -> The value is: 42 showValue empty -> No value
Haskell
In Haskell, the option type is defined as .
showValue :: Maybe Int -> String showValue = foldl (\_ x -> "The value is: " ++ show x) "No value"
main :: IO () main = do let full = Just 42 let empty = Nothing
putStrLn $ "showValue full -> " ++ showValue full putStrLn $ "showValue empty -> " ++ showValue empty
showValue full -> The value is: 42 showValue empty -> No value
Idris
In Idris, the option type is defined as .
showValue : Maybe Int -> String showValue = foldl (\_, x => "The value is " ++ show x) "No value"
main : IO () main = do let full = Just 42 let empty = Nothing
putStrLn $ "showValue full -> " ++ showValue full putStrLn $ "showValue empty -> " ++ showValue empty
showValue full -> The value is: 42 showValue empty -> No value
Java
In Java, the option type is defined the standard library by the class.
import java.util.Optional;
public class OptionExample { static String showValue(Optional<Integer> opt) { return opt.map(x -> String.format("The value is: %d", x)).orElse("No value");
public static void main(String[] args) { Optional<Integer> full = Optional.of(42); Optional<Integer> empty = Optional.empty();
System.out.printf("showValue(full): %s\n", showValue(full)); System.out.printf("showValue(empty): %s\n", showValue(empty));
showValue full -> The value is: 42 showValue empty -> No value
Nim
import std/options
proc showValue(opt: Option[int]): string = opt.map(proc (x: int): string = "The value is: " & $x).get("No value")
let full = some(42) empty = none(int)
echo "showValue(full) -> ", showValue(full) echo "showValue(empty) -> ", showValue(empty)
showValue(full) -> The Value is: 42 showValue(empty) -> No value
OCaml
In OCaml, the option type is defined as .
let show_value = Option.fold ~none:"No value" ~some:(fun x -> "The value is: " ^ string_of_int x)
let () = let full = Some 42 in let empty = None in
print_endline ("show_value full -> " ^ show_value full); print_endline ("show_value empty -> " ^ show_value empty)
show_value full -> The value is: 42 show_value empty -> No value
Rocq
In Rocq, the option type is defined as .
Rust
In Rust, the option type is defined as .
fn show_value(opt: Option<i32>) -> String { opt.map_or("No value".to_owned(), |x: i32| format!("The value is: {}", x)) }
fn main() { let full: Option<i32> = Some(42); let empty: Option<i32> = None;
println!("show_value(full) -> {}", show_value(full)); println!("show_value(empty) -> {}", show_value(empty)); }
show_value(full) -> The value is: 42 show_value(empty) -> No value
Scala
In Scala, the option type is defined as , a type extended by and .
object Main: def showValue(opt: Option[Int]): String = opt.fold("No value")(x => s"The value is: $x")
def main(args: Array[String]): Unit = val full = Some(42) val empty = None
println(s"showValue(full) -> ${showValue(full)}") println(s"showValue(empty) -> ${showValue(empty)}")
showValue(full) -> The value is: 42 showValue(empty) -> No value
Standard ML
In Standard ML, the option type is defined as .
Swift
In Swift, the option type is defined as but is generally written as .
func showValue(_ opt: Int?) -> String { return opt.map { "The value is: \($0)" } ?? "No value" }
let full = 42 let empty: Int? = nil
print("showValue(full) -> \(showValue(full))") print("showValue(empty) -> \(showValue(empty))")
showValue(full) -> The value is: 42 showValue(empty) -> No value
Zig
In Zig, add ? before the type name like ?i32 to make it an optional type.
Payload n can be captured in an if or while statement, such as , and an else clause is evaluated if it is null.
const std = @import("std");
fn showValue(gpa: std.mem.Allocator, opt: ?i32) ![]u8 { return if (opt) |n| std.fmt.allocPrint(gpa, "The value is: {}", .{n}) else gpa.dupe(u8, "No value"); }
pub fn main(init: std.process.Init) !void { // Prepare the standard output stream. var buffer: u8 = undefined; var writer = std.Io.File.stdout().writer(init.io, &buffer);
// Perform our example. const full = 42; const empty = null;
const full_msg = try showValue(init.gpa, full); defer init.gpa.free(full_msg); try writer.interface.print("showValue(init.gpa, full) -> {s}\n", .{full_msg});
const empty_msg = try showValue(init.gpa, empty); defer init.gpa.free(empty_msg); try writer.interface.print("showValue(init.gpa, empty) -> {s}\n", .{empty_msg});
try writer.interface.flush(); }
showValue(init.gpa, full) -> The value is: 42 showValue(init.gpa, empty) -> No value
See also Result type Tagged union Nullable type Null object pattern Exception handling Pattern matching
References