Tipo de opción - Option type
En los lenguajes de programación (especialmente los lenguajes de programación funcional ) y la teoría de tipos , un tipo de opción o tal vez tipo es un tipo polimórfico que representa la encapsulación de un valor opcional; por ejemplo, se utiliza como el tipo de retorno de funciones que pueden o no devolver un valor significativo cuando se aplican. Consiste en un constructor que está vacío (a menudo llamado None
o Nothing
), o que encapsula el tipo de datos original A
(a menudo escrito Just A
o Some A
).
Un concepto distinto, pero relacionado fuera de la programación funcional, que es popular en la programación orientada a objetos , se denomina tipos que aceptan valores NULL (a menudo expresados como A?
). La principal diferencia entre los tipos de opciones y los tipos que aceptan valores NULL es que los tipos de opciones admiten la anidación ( Maybe (Maybe A)
≠ Maybe A
), mientras que los tipos que aceptan valores NULL no ( String??
= String?
).
Aspectos teóricos
En la teoría de tipos , puede ser escrito como: . Esto expresa el hecho de que para un conjunto dado de valores en , un tipo de opción agrega exactamente un valor adicional (el valor vacío) al conjunto de valores válidos para . Esto se refleja en la programación por el hecho de que en los lenguajes que tienen uniones etiquetadas , los tipos de opciones se pueden expresar como la unión etiquetada del tipo encapsulado más un tipo de unidad .
En la correspondencia Curry-Howard , los tipos de opciones están relacionados con la ley de aniquilación para ∨: x∨1 = 1.
Un tipo de opción también puede verse como una colección que contiene uno o cero elementos.
El tipo de opción también es una mónada donde:
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
La naturaleza monádica del tipo de opción es útil para rastrear de manera eficiente fallas y errores.
Nombres y definiciones
En diferentes lenguajes de programación, el tipo de opción tiene varios nombres y definiciones.
- En Agda , se nombra
Maybe
con variantesnothing
yjust a
. - En C ++ 17 se define como la clase de plantilla , se puede usar para crear una opción vacía. ( Podría romper las leyes de las mónadas debido a la gran sobrecarga de los constructores ) .
std::optional<T>
optional()
- En C # , se define como, pero generalmente se escribe como . ( Rompe las leyes de las mónadas ) .
Nullable<T>
T?
- En Coq , se define como .
Inductive option (A:Type) : Type := | Some : A -> option A | None : option A.
- En Elm , se nombra
Maybe
y se define como .type Maybe a = Just a | Nothing
- En Haskell , se nombra
Maybe
y se define como .data Maybe a = Nothing | Just a
- En Idris , se define como .
data Maybe a = Nothing | Just a
- En Java , desde la versión 8, se define como clase final parametrizada . ( Rompe las leyes de las mónadas (el mapa está implementado incorrectamente). )
Optional<T>
- En Julia , se llama . ( Sin embargo, esto ha quedado obsoleto ) .
Nullable{T}
- En OCaml , se define como .
type 'a option = None | Some of 'a
- En Perl 6 , este es el valor predeterminado, pero puede agregar un "emoticón" para optar por un tipo sin opción. ( Rompe las leyes de las mónadas (no admite el anidamiento ))
:D
- En Rust , se define como .
enum Option<T> { None, Some(T) }
- En Scala , se define como un tipo extendido por y .
sealed abstract class Option[+A]
final case class Some[+A](value: A)
case object None
- En ML estándar , se define como .
datatype 'a option = NONE | SOME of 'a
- En Swift , se define como, pero generalmente se escribe como .
enum Optional<T> { case none, some(T) }
T?
Ejemplos
Ada
Ada no implementa tipos de opción directamente, sin embargo, proporciona tipos discriminados que se pueden usar para parametrizar un registro. Para implementar un tipo de Opción, se utiliza un tipo booleano como discriminante; el siguiente ejemplo proporciona un genérico para crear un tipo de opción a partir de cualquier tipo restringido no limitado:
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;
Scala
Scala se implementa Option
como un tipo parametrizado, por lo que una variable puede ser una Option
, a la que se accede de la siguiente manera:
object Main {
// This function uses pattern matching to deconstruct `Option`s
def computeV1(opt: Option[Int]): String =
opt match {
case Some(x) => s"The value is: $x"
case None => "No value"
}
// This function uses the built-in `fold` method
def computeV2(opt: Option[Int]): String =
opt.fold("No value")(x => s"The value is: $x")
def main(args: Array[String]): Unit = {
// Define variables that are `Option`s of type `Int`
val full = Some(42)
val empty: Option[Int] = None
// computeV1(full) -> The value is: 42
println(s"computeV1(full) -> ${computeV1(full)}")
// computeV1(empty) -> No value
println(s"computeV1(empty) -> ${computeV1(empty)}")
// computeV2(full) -> The value is: 42
println(s"computeV2(full) -> ${computeV2(full)}")
// computeV2(empty) -> No value
println(s"computeV2(empty) -> ${computeV2(empty)}")
}
}
Existen dos formas principales de utilizar un Option
valor. La primera, no la mejor, es la coincidencia de patrones , como en el primer ejemplo. La segunda, la mejor práctica es un enfoque monádico, como en el segundo ejemplo. De esta forma, un programa es seguro, ya que no puede generar ninguna excepción o error (por ejemplo, al intentar obtener el valor de una Option
variable que sea igual a None
). Por lo tanto, esencialmente funciona como una alternativa de tipo seguro al valor nulo.
OCaml
OCaml se implementa Option
como un tipo de variante parametrizado. Option
s se construyen y deconstruyen de la siguiente manera:
(* This function uses pattern matching to deconstruct `option`s *)
let compute_v1 = function
| Some x -> "The value is: " ^ string_of_int x
| None -> "No value"
(* This function uses the built-in `fold` function *)
let compute_v2 =
Option.fold ~none:"No value" ~some:(fun x -> "The value is: " ^ string_of_int x)
let () =
(* Define variables that are `option`s of type `int` *)
let full = Some 42 in
let empty = None in
(* compute_v1 full -> The value is: 42 *)
print_endline ("compute_v1 full -> " ^ compute_v1 full);
(* compute_v1 empty -> No value *)
print_endline ("compute_v1 empty -> " ^ compute_v1 empty);
(* compute_v2 full -> The value is: 42 *)
print_endline ("compute_v2 full -> " ^ compute_v2 full);
(* compute_v2 empty -> No value *)
print_endline ("compute_v2 empty -> " ^ compute_v2 empty)
F#
// This function uses pattern matching to deconstruct `option`s
let compute_v1 = function
| Some x -> sprintf "The value is: %d" x
| None -> "No value"
// This function uses the built-in `fold` function
let compute_v2 =
Option.fold (fun _ x -> sprintf "The value is: %d" x) "No value"
// Define variables that are `option`s of type `int`
let full = Some 42
let empty = None
// compute_v1 full -> The value is: 42
compute_v1 full |> printfn "compute_v1 full -> %s"
// compute_v1 empty -> No value
compute_v1 empty |> printfn "compute_v1 empty -> %s"
// compute_v2 full -> The value is: 42
compute_v2 full |> printfn "compute_v2 full -> %s"
// compute_v2 empty -> No value
compute_v2 empty |> printfn "compute_v2 empty -> %s"
Haskell
-- This function uses pattern matching to deconstruct `Maybe`s
computeV1 :: Maybe Int -> String
computeV1 (Just x) = "The value is: " ++ show x
computeV1 Nothing = "No value"
-- This function uses the built-in `foldl` function
computeV2 :: Maybe Int -> String
computeV2 = foldl (\_ x -> "The value is: " ++ show x) "No value"
main :: IO ()
main = do
-- Define variables that are `Maybe`s of type `Int`
let full = Just 42
let empty = Nothing
-- computeV1 full -> The value is: 42
putStrLn $ "computeV1 full -> " ++ computeV1 full
-- computeV1 full -> No value
putStrLn $ "computeV1 empty -> " ++ computeV1 empty
-- computeV2 full -> The value is: 42
putStrLn $ "computeV2 full -> " ++ computeV2 full
-- computeV2 full -> No value
putStrLn $ "computeV2 empty -> " ++ computeV2 empty
Rápido
// This function uses a `switch` statement to deconstruct `Optional`s
func computeV1(_ opt: Int?) -> String {
switch opt {
case .some(let x):
return "The value is: \(x)"
case .none:
return "No value"
}
}
// This function uses optional binding to deconstruct `Optional`s
func computeV2(_ opt: Int?) -> String {
if let x = opt {
return "The value is: \(x)"
} else {
return "No value"
}
}
// Define variables that are `Optional`s of type `Int`
let full: Int? = 42
let empty: Int? = nil
// computeV1(full) -> The value is: 42
print("computeV1(full) -> \(computeV1(full))")
// computeV1(empty) -> No value
print("computeV1(empty) -> \(computeV1(empty))")
// computeV2(full) -> The value is: 42
print("computeV2(full) -> \(computeV2(full))")
// computeV2(empty) -> No value
print("computeV2(empty) -> \(computeV2(empty))")
Oxido
// This function uses a `match` expression to deconstruct `Option`s
fn compute_v1(opt: &Option<i32>) -> String {
match opt {
Some(x) => format!("The value is: {}", x),
None => "No value".to_owned(),
}
}
// This function uses an `if let` expression to deconstruct `Option`s
fn compute_v2(opt: &Option<i32>) -> String {
if let Some(x) = opt {
format!("The value is: {}", x)
} else {
"No value".to_owned()
}
}
// This function uses the built-in `map_or` method
fn compute_v3(opt: &Option<i32>) -> String {
opt.map_or("No value".to_owned(), |x| format!("The value is: {}", x))
}
fn main() {
// Define variables that are `Option`s of type `i32`
let full = Some(42);
let empty: Option<i32> = None;
// compute_v1(&full) -> The value is: 42
println!("compute_v1(&full) -> {}", compute_v1(&full));
// compute_v1(&empty) -> No value
println!("compute_v1(&empty) -> {}", compute_v1(&empty));
// compute_v2(&full) -> The value is: 42
println!("compute_v2(&full) -> {}", compute_v2(&full));
// compute_v2(&empty) -> No value
println!("compute_v2(&empty) -> {}", compute_v2(&empty));
// compute_v3(&full) -> The value is: 42
println!("compute_v3(&full) -> {}", compute_v3(&full));
// compute_v3(&empty) -> No value
println!("compute_v3(&empty) -> {}", compute_v3(&empty))
}
Nim
import options
# This proc uses the built-in `isSome` and `get` procs to deconstruct `Option`s
proc compute(opt: Option[int]): string =
if opt.isSome:
"The Value is: " & $opt.get
else:
"No value"
# Define variables that are `Optional`s of type `Int`
let
full = some(42)
empty = none(int)
# compute(full) -> The Value is: 42
echo "compute(full) -> ", compute(full)
# compute(empty) -> No value
echo "compute(empty) -> ", compute(empty)
Ver también
- Tipo de resultado
- Unión etiquetada
- Tipo que acepta valores NULL
- Patrón de objeto nulo
- Manejo de excepciones