F#
Tipos de opciones
Buscar..
Definición de Opción
Una Option es una unión discriminada con dos casos, None o Some .
type Option<'T> = Some of 'T | None
Utilice la opción <'T> sobre valores nulos
En los lenguajes de programación funcional, como F# null valores null se consideran potencialmente dañinos y de estilo deficiente (no idiomático).
Considere este código C# :
string x = SomeFunction ();
int l = x.Length;
x.Length lanzará si x es null , agreguemos protección:
string x = SomeFunction ();
int l = x != null ? x.Length : 0;
O:
string x = SomeFunction () ?? "";
int l = x.Length;
O:
string x = SomeFunction ();
int l = x?.Length;
En null valores null F# idiomáticos no se utilizan, por lo que nuestro código se ve así:
let x = SomeFunction ()
let l = x.Length
Sin embargo, a veces es necesario representar valores vacíos o no válidos. Entonces podemos usar la Option<'T> :
let SomeFunction () : string option = ...
SomeFunction devuelve Some valor de string o None . Extraemos el valor de la string utilizando el patrón de coincidencia
let v =
match SomeFunction () with
| Some x -> x.Length
| None -> 0
La razón por la cual este código es menos frágil que:
string x = SomeFunction ();
int l = x.Length;
Es porque no podemos llamar Length en una string option . Necesitamos extraer el valor de la string utilizando la coincidencia de patrones y al hacerlo, tenemos la garantía de que el valor de la string es seguro de usar.
El módulo opcional habilita la programación orientada al ferrocarril
El manejo de errores es importante pero puede convertir un algoritmo elegante en un desastre. La Programación Orientada a Ferrocarriles ( ROP ) se utiliza para hacer que el manejo de errores sea elegante y componible.
Considere la función simple f :
let tryParse s =
let b, v = System.Int32.TryParse s
if b then Some v else None
let f (g : string option) : float option =
match g with
| None -> None
| Some s ->
match tryParse s with // Parses string to int
| None -> None
| Some v when v < 0 -> None // Checks that int is greater than 0
| Some v -> v |> float |> Some // Maps int to float
El propósito de f es analizar el valor de la string entrada (si hay Some ) en un int . Si el int es mayor que 0 lanzamos en un float . En todos los demás casos, rescatamos con None .
Aunque es una función extremadamente simple, la match anidada disminuye significativamente la legibilidad.
ROP observa que tenemos dos tipos de vías de ejecución en nuestro programa
- Camino feliz - eventualmente calcularemos
Somevalor - Ruta de error: todas las demás rutas producen
None
Como las rutas de error son más frecuentes, tienden a tomar el control del código. Nos gustaría que el código de ruta feliz sea la ruta de código más visible.
Una función equivalente g usa ROP podría verse así:
let g (v : string option) : float option =
v
|> Option.bind tryParse // Parses string to int
|> Option.filter ((<) 0) // Checks that int is greater than 0
|> Option.map float // Maps int to float
Se parece mucho a cómo tendemos a procesar listas y secuencias en F# .
Uno puede ver una Option<'T> como una List<'T> que solo puede contener 0 o 1 elemento donde Option.bind comporta como List.pick (conceptualmente Option.bind asigna mejor a List.collect pero List.pick podría ser Más fácil de entender).
bind , filter y map manejan las rutas de error g solo contienen el código de ruta feliz.
Todas las funciones que aceptan directamente la Option<_> y devuelven la Option<_> pueden componer directamente con |> y >> .
ROP por lo tanto aumenta la legibilidad y la composibilidad.
Usando tipos de opciones de C #
No es una buena idea exponer los tipos de opción al código C #, ya que C # no tiene una forma de manejarlos. Las opciones son introducir FSharp.Core como una dependencia en su proyecto de C # (que es lo que tendría que hacer si está consumiendo una biblioteca de F # no diseñada para interoperar con C #), o cambiar los valores de None a null .
Pre-F # 4.0
La forma de hacerlo es crear una función de conversión propia:
let OptionToObject opt =
match opt with
| Some x -> x
| None -> null
Para los tipos de valor tendría que recurrir a boxearlos o usar System.Nullable .
let OptionToNullable x =
match x with
| Some i -> System.Nullable i
| None -> System.Nullable ()
F # 4.0
En F # 4.0, las funciones ofObj , toObj , ofNullable y toNullable introdujeron en el módulo de Option . En F # interactivo se pueden utilizar de la siguiente manera:
let l1 = [ Some 1 ; None ; Some 2]
let l2 = l1 |> List.map Option.toNullable;;
// val l1 : int option list = [Some 1; null; Some 2]
// val l2 : System.Nullable<int> list = [1; null; 2]
let l3 = l2 |> List.map Option.ofNullable;;
// val l3 : int option list = [Some 1; null; Some 2]
// Equality
l1 = l2 // fails to compile: different types
l1 = l3 // true
Tenga en cuenta que None compila a null internamente. Sin embargo, en lo que respecta a F #, es un None .
let lA = [Some "a"; None; Some "b"]
let lB = lA |> List.map Option.toObj
// val lA : string option list = [Some "a"; null; Some "b"]
// val lB : string list = ["a"; null; "b"]
let lC = lB |> List.map Option.ofObj
// val lC : string option list = [Some "a"; null; Some "b"]
// Equality
lA = lB // fails to compile: different types
lA = lC // true