Buscar..
Cómo componer valores y funciones utilizando operadores comunes.
En Programación Orientada a Objetos una tarea común es componer objetos (valores). En la Programación Funcional es una tarea común componer valores y funciones.
Estamos acostumbrados a componer los valores de nuestra experiencia de otros lenguajes de programación que utilizan operadores como +
, -
, *
, /
y así sucesivamente.
Composición del valor
let x = 1 + 2 + 3 * 2
Como la programación funcional compone funciones y valores, no es sorprendente que haya operadores comunes para la composición de funciones como >>
, <<
, |>
y <|
.
Composición de funciones
// val f : int -> int
let f v = v + 1
// val g : int -> int
let g v = v * 2
// Different ways to compose f and g
// val h : int -> int
let h1 v = g (f v)
let h2 v = v |> f |> g // Forward piping of 'v'
let h3 v = g <| (f <| v) // Reverse piping of 'v' (because <| has left associcativity we need ())
let h4 = f >> g // Forward functional composition
let h5 = g << f // Reverse functional composition (closer to math notation of 'g o f')
En F#
tubería hacia adelante se prefiere sobre la tubería inversa porque:
- La inferencia de tipo (generalmente) fluye de izquierda a derecha, por lo que es natural que los valores y las funciones fluyan de izquierda a derecha también
- Porque
<|
y<<
debería tener asociatividad a la derecha, pero enF#
son asociativas a la izquierda, lo que nos obliga a insertar () - La mezcla de tuberías hacia adelante y hacia atrás generalmente no funciona porque tienen la misma prioridad.
Composición de mónada
Como las mónadas (como la Option<'T>
o la List<'T>
) se usan comúnmente en la programación funcional, también hay operadores comunes pero menos conocidos para componer funciones que trabajan con Mónadas como >>=
, >=>
, <|>
y <*>
.
let (>>=) t uf = Option.bind uf t
let (>=>) tf uf = fun v -> tf v >>= uf
// val oinc : int -> int option
let oinc v = Some (v + 1) // Increment v
// val ofloat : int -> float option
let ofloat v = Some (float v) // Map v to float
// Different ways to compose functions working with Option Monad
// val m : int option -> float option
let m1 v = Option.bind (fun v -> Some (float (v + 1))) v
let m2 v = v |> Option.bind oinc |> Option.bind ofloat
let m3 v = v >>= oinc >>= ofloat
let m4 = oinc >=> ofloat
// Other common operators are <|> (orElse) and <*> (andAlso)
// If 't' has Some value then return t otherwise return u
let (<|>) t u =
match t with
| Some _ -> t
| None -> u
// If 't' and 'u' has Some values then return Some (tv*uv) otherwise return None
let (<*>) t u =
match t, u with
| Some tv, Some tu -> Some (tv, tu)
| _ -> None
// val pickOne : 'a option -> 'a option -> 'a option
let pickOne t u v = t <|> u <|> v
// val combine : 'a option -> 'b option -> 'c option -> (('a*'b)*'c) option
let combine t u v = t <*> u <*> v
Conclusión
Para los nuevos programadores funcionales, la composición de funciones con operadores puede parecer opaca y oscura, pero eso se debe a que el significado de estos operadores no es tan comúnmente conocido como operadores que trabajan con valores. Sin embargo, con un poco de entrenamiento usando |>
, >>
, >>=
y >=>
vuelve tan natural como usar +
, -
, *
y /
.
Latebinding en F # utilizando? operador
En un lenguaje de tipo estático como F#
, trabajamos con tipos conocidos en tiempo de compilación. Consumimos fuentes de datos externas de una manera segura para el tipo utilizando proveedores de tipo.
Sin embargo, ocasionalmente hay necesidad de usar un enlace tardío (como dynamic
en C#
). Por ejemplo, cuando se trabaja con documentos JSON
que no tienen un esquema bien definido.
Para simplificar el trabajo con el enlace tardío, ¿ F#
proporciona soporte para operadores de búsqueda dinámica ?
y ?<-
.
Ejemplo:
// (?) allows us to lookup values in a map like this: map?MyKey
let inline (?) m k = Map.tryFind k m
// (?<-) allows us to update values in a map like this: map?MyKey <- 123
let inline (?<-) m k v = Map.add k v m
let getAndUpdate (map : Map<string, int>) : int option*Map<string, int> =
let i = map?Hello // Equivalent to map |> Map.tryFind "Hello"
let m = map?Hello <- 3 // Equivalent to map |> Map.add "Hello" 3
i, m
Resulta que el soporte de F#
para el enlace tardío es simple pero flexible.