Option type

Last updated

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).

Contents

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 (Maybe (Maybe A)Maybe A), while nullable types do not (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. [1]

In the Curry–Howard correspondence, option types are related to the annihilation law for ∨: x∨1=1.[ how? ]

An option type can also be seen as a collection containing either one or zero elements.[ original research? ]

The option type is also a monad where: [2]

return=Just-- Wraps the value into a maybeNothing>>=f=Nothing-- Fails if the previous monad fails(Justx)>>=f=fx-- Succeeds when both monads succeed

The monadic nature of the option type is useful for efficiently tracking failure and errors. [3]

Names and definitions

In different programming languages, the option type has various names and definitions.

Examples

Ada

Ada does not implement option-types directly, however it provides discriminated types which can be used to parameterize a record. To implement a 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.TypeElement_Typeisprivate;PackageOptional_Typeis-- When the discriminant, Has_Element, is true there is an element field,-- when it is false, there are no fields (hence the null keyword).TypeOptional(Has_Element: Boolean)isrecordcaseHas_ElementiswhenFalse=>Null;whenTrue=>Element:Element_Type;endcase;end record;endOptional_Type;

Scala

Scala implements Option as a parameterized type, so a variable can be an Option, accessed as follows: [7]

objectMain{// This function uses pattern matching to deconstruct `Option`sdefcomputeV1(opt:Option[Int]):String=optmatch{caseSome(x)=>s"The value is: $x"caseNone=>"No value"}// This function uses the built-in `fold` methoddefcomputeV2(opt:Option[Int]):String=opt.fold("No value")(x=>s"The value is: $x")defmain(args:Array[String]):Unit={// Define variables that are `Option`s of type `Int`valfull=Some(42)valempty:Option[Int]=None// computeV1(full) -> The value is: 42println(s"computeV1(full) -> ${computeV1(full)}")// computeV1(empty) -> No valueprintln(s"computeV1(empty) -> ${computeV1(empty)}")// computeV2(full) -> The value is: 42println(s"computeV2(full) -> ${computeV2(full)}")// computeV2(empty) -> No valueprintln(s"computeV2(empty) -> ${computeV2(empty)}")}}

Two main ways to use an Option value exist. The first, not the best, is the pattern matching, as in the first example. The second, the best practice is a monadic approach, as in the second example. In this way, a program is safe, as it can generate no exception or error (e.g., by trying to obtain the value of an Option variable that is equal to None). Thus, it essentially works as a type-safe alternative to the null value.

OCaml

OCaml implements Option as a parameterized variant type. Options are constructed and deconstructed as follows:

(* This function uses pattern matching to deconstruct `option`s *)letcompute_v1=function|Somex->"The value is: "^string_of_intx|None->"No value"(* This function uses the built-in `fold` function *)letcompute_v2=Option.fold~none:"No value"~some:(funx->"The value is: "^string_of_intx)let()=(* Define variables that are `option`s of type `int` *)letfull=Some42inletempty=Nonein(* compute_v1 full -> The value is: 42 *)print_endline("compute_v1 full -> "^compute_v1full);(* compute_v1 empty -> No value *)print_endline("compute_v1 empty -> "^compute_v1empty);(* compute_v2 full -> The value is: 42 *)print_endline("compute_v2 full -> "^compute_v2full);(* compute_v2 empty -> No value *)print_endline("compute_v2 empty -> "^compute_v2empty)

F#

// This function uses pattern matching to deconstruct `option`sletcompute_v1=function|Somex->sprintf"The value is: %d"x|None->"No value"// This function uses the built-in `fold` functionletcompute_v2=Option.fold(fun_x->sprintf"The value is: %d"x)"No value"// Define variables that are `option`s of type `int`letfull=Some42letempty=None// compute_v1 full -> The value is: 42compute_v1full|>printfn"compute_v1 full -> %s"// compute_v1 empty -> No valuecompute_v1empty|>printfn"compute_v1 empty -> %s"// compute_v2 full -> The value is: 42compute_v2full|>printfn"compute_v2 full -> %s"// compute_v2 empty -> No valuecompute_v2empty|>printfn"compute_v2 empty -> %s"

Haskell

-- This function uses pattern matching to deconstruct `Maybe`scomputeV1::MaybeInt->StringcomputeV1(Justx)="The value is: "++showxcomputeV1Nothing="No value"-- This function uses the built-in `foldl` functioncomputeV2::MaybeInt->StringcomputeV2=foldl(\_x->"The value is: "++showx)"No value"main::IO()main=do-- Define variables that are `Maybe`s of type `Int`letfull=Just42letempty=Nothing-- computeV1 full -> The value is: 42putStrLn$"computeV1 full -> "++computeV1full-- computeV1 full -> No valueputStrLn$"computeV1 empty -> "++computeV1empty-- computeV2 full -> The value is: 42putStrLn$"computeV2 full -> "++computeV2full-- computeV2 full -> No valueputStrLn$"computeV2 empty -> "++computeV2empty

Swift

// This function uses a `switch` statement to deconstruct `Optional`sfunccomputeV1(_opt:Int?)->String{switchopt{case.some(letx):return"The value is: \(x)"case.none:return"No value"}}// This function uses optional binding to deconstruct `Optional`sfunccomputeV2(_opt:Int?)->String{ifletx=opt{return"The value is: \(x)"}else{return"No value"}}// Define variables that are `Optional`s of type `Int`letfull:Int?=42letempty:Int?=nil// computeV1(full) -> The value is: 42print("computeV1(full) -> \(computeV1(full))")// computeV1(empty) -> No valueprint("computeV1(empty) -> \(computeV1(empty))")// computeV2(full) -> The value is: 42print("computeV2(full) -> \(computeV2(full))")// computeV2(empty) -> No valueprint("computeV2(empty) -> \(computeV2(empty))")

Rust

// This function uses a `match` expression to deconstruct `Option`sfncompute_v1(opt: &Option<i32>)-> String{matchopt{Some(x)=>format!("The value is: {}",x),None=>"No value".to_owned(),}}// This function uses an `if let` expression to deconstruct `Option`sfncompute_v2(opt: &Option<i32>)-> String{ifletSome(x)=opt{format!("The value is: {}",x)}else{"No value".to_owned()}}// This function uses the built-in `map_or` methodfncompute_v3(opt: &Option<i32>)-> String{opt.map_or("No value".to_owned(),|x|format!("The value is: {}",x))}fnmain(){// Define variables that are `Option`s of type `i32`letfull=Some(42);letempty: Option<i32>=None;// compute_v1(&full) -> The value is: 42println!("compute_v1(&full) -> {}",compute_v1(&full));// compute_v1(&empty) -> No valueprintln!("compute_v1(&empty) -> {}",compute_v1(&empty));// compute_v2(&full) -> The value is: 42println!("compute_v2(&full) -> {}",compute_v2(&full));// compute_v2(&empty) -> No valueprintln!("compute_v2(&empty) -> {}",compute_v2(&empty));// compute_v3(&full) -> The value is: 42println!("compute_v3(&full) -> {}",compute_v3(&full));// compute_v3(&empty) -> No valueprintln!("compute_v3(&empty) -> {}",compute_v3(&empty))}

Nim

importoptions# This proc uses the built-in `isSome` and `get` procs to deconstruct `Option`sproc compute(opt:Option[int]):string=ifopt.isSome:"The Value is: "&$opt.getelse:"No value"# Define variables that are `Optional`s of type `Int`letfull=some(42)empty=none(int)# compute(full) -> The Value is: 42echo"compute(full) -> ",compute(full)# compute(empty) -> No valueecho"compute(empty) -> ",compute(empty)

See also

Related Research Articles

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. It is a kind of lazy evaluation that refers specifically to the instantiation of objects or other resources.

In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements. The iterator pattern decouples algorithms from containers; in some cases, algorithms are necessarily container-specific and thus cannot be decoupled.

F Sharp (programming language) Microsoft programming language

F# is a functional-first, general purpose, strongly typed, multi-paradigm programming language that encompasses functional, imperative, and object-oriented programming methods. F# is most often used as a cross-platform Common Language Infrastructure (CLI) language on .NET Core, but it can also generate JavaScript and graphics processing unit (GPU) code.

In mathematics and computer science, a higher-order function is a function that does at least one of the following:

In computer science, a tagged union, also called a variant, variant record, choice type, discriminated union, disjoint union, sum type or coproduct, is a data structure used to hold a value that could take on several different, but fixed, types. Only one of the types can be in use at any one time, and a tag field explicitly indicates which one is in use. It can be thought of as a type that has several "cases", each of which should be handled correctly when that type is manipulated. This is critical in defining recursive datatypes, in which some component of a value may have the same type as the value itself, for example in defining a type for representing trees, where it is necessary to distinguish multi-node subtrees and leafs. Like ordinary unions, tagged unions can save storage by overlapping storage areas for each type, since only one is in use at a time.

In functional programming, a monad is an abstraction that allows structuring programs generically. Supporting languages may use monads to abstract away boilerplate code needed by the program logic. Monads achieve this by providing their own data type, which represents a specific form of computation, along with one procedure to wrap values of any basic type within the monad and another to compose functions that output monadic values.

Foreach loop control flow statement

Foreach loop is a control flow statement for traversing items in a collection. Foreach is usually used in place of a standard for loop statement. Unlike other for loop constructs, however, foreach loops usually maintain no explicit counter: they essentially say "do this to everything in this set", rather than "do this x times". This avoids potential off-by-one errors and makes code simpler to read. In object-oriented languages an iterator, even if implicit, is often used as the means of traversal.

In class-based object-oriented programming, a constructor is a special type of subroutine called to create an object. It prepares the new object for use, often accepting arguments that the constructor uses to set required member variables.

Java syntax

The syntax of Java refers to the set of rules defining how a Java program is written and interpreted.

In computer programming, an entry point is where the first instructions of a program are executed, and where the program has access to command line arguments.

Exception handling syntax is the set of keywords and/or structures provided by a computer programming language to allow exception handling, which separates the handling of errors that arise during a program's operation from its ordinary processes. Syntax for exception handling varies between programming languages, partly to cover semantic differences but largely to fit into each language's overall syntactic structure. Some languages do not call the relevant concept "exception handling"; others may not have direct facilities for it, but can still provide means to implement it.

In computer programming, a semipredicate problem occurs when a subroutine intended to return a useful value can fail, but the signalling of failure uses an otherwise valid return value. The problem is that the caller of the subroutine cannot tell what the result means in this case.

Nullable types are a feature of some programming languages which allow the value to be set to the special value NULL instead of the usual possible values of the data type. In statically typed languages, a nullable type is an option type, while in dynamically typed languages, equivalent behavior is provided by having a single null value.

In computer programming, an anonymous function is a function definition that is not bound to an identifier. Anonymous functions are often arguments being passed to higher-order functions, or used for constructing the result of a higher-order function that needs to return a function. If the function is only used once, or a limited number of times, an anonymous function may be syntactically lighter than using a named function. Anonymous functions are ubiquitous in functional programming languages and other languages with first-class functions, where they fulfil the same role for the function type as literals do for other data types.

Generics are a facility of generic programming that were added to the Java programming language in 2004 within version J2SE 5.0. They were designed to extend Java's type system to allow "a type or method to operate on objects of various types while providing compile-time type safety". The aspect compile-time type safety was not fully achieved, since it was shown in 2016 that it is not guaranteed in all cases.

The null coalescing operator is a binary operator that is part of the syntax for a basic conditional expression in several programming languages, including C#, PowerShell as of version 7.0.0, Perl as of version 5.10, Swift, and PHP 7.0.0. While its behavior differs between implementations, the null coalescing operator generally returns the result of its left-most operand if it exists and is not null, and otherwise returns the right-most operand. This behavior allows a default value to be defined for cases where a more specific value is not available.

This article describes the features in Haskell.

Elm (programming language)

Elm is a domain-specific programming language for declaratively creating web browser-based graphical user interfaces. Elm is purely functional, and is developed with emphasis on usability, performance, and robustness. It advertises "no runtime exceptions in practice", made possible by the Elm compiler's static type checking.

Kotlin is a cross-platform, statically typed, general-purpose programming language with type inference. Kotlin is designed to interoperate fully with Java, and the JVM version of Kotlin's standard library depends on the Java Class Library, but type inference allows its syntax to be more concise. Kotlin mainly targets the JVM, but also compiles to JavaScript or native code, e.g. for native iOS apps sharing business logic with Android apps. Language development costs are borne by JetBrains, while the Kotlin Foundation protects the Kotlin trademark.

Ur also called Ur/Web is a Free and Open source functional programming language specific for web development, created by Adam Chlipala at the Massachusetts Institute of Technology that from a single program produces server code, browser client code and SQL code specific for the chosen database backend.

References

  1. Milewski, Bartosz (2015-01-13). "Simple Algebraic Data Types". Bartosz Milewski's Programming Cafe. Sum types. "We could have encoded Maybe as: data Maybe a = Either () a". Archived from the original on 2019-08-18. Retrieved 2019-08-18.
  2. "A Fistful of Monads - Learn You a Haskell for Great Good!". www.learnyouahaskell.com. Retrieved 2019-08-18.
  3. Hutton, Graham (Nov 25, 2017). "What is a Monad?". Computerphile Youtube. Retrieved Aug 18, 2019.
  4. "Maybe · An Introduction to Elm". guide.elm-lang.org.
  5. "Julia v0.7.0 Release Notes · The Julia Language". docs.julialang.org.
  6. "Apple Developer Documentation". developer.apple.com. Retrieved 2020-09-06.
  7. Martin Odersky; Lex Spoon; Bill Venners (2008). Programming in Scala. Artima Inc. pp. 282–284. ISBN   978-0-9815316-0-1 . Retrieved 6 September 2011.