Ricerca…
Come comporre valori e funzioni utilizzando operatori comuni
Nella programmazione orientata agli oggetti un compito comune è comporre oggetti (valori). Nella programmazione funzionale è compito comune comporre valori e funzioni.
Siamo abituati a comporre valori dalla nostra esperienza di altri linguaggi di programmazione usando operatori come +
, -
, *
, /
e così via.
Composizione del valore
let x = 1 + 2 + 3 * 2
Poiché la programmazione funzionale compone funzioni e valori, non sorprende che esistano operatori comuni per la composizione di funzioni come >>
, <<
, |>
e <|
.
Composizione funzionale
// 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')
In F#
tubazioni in avanti sono preferite rispetto alle tubazioni inverse perché:
- Digitare un'inferenza (generalmente) scorre da sinistra a destra, quindi è naturale che valori e funzioni scorrano anche da sinistra a destra
- Perché
<|
e<<
dovrebbe avere la giusta associatività, ma inF#
sono lasciati associativi che ci costringono a inserire () - La miscelazione delle tubazioni avanti e indietro generalmente non funziona perché hanno la stessa precedenza.
Composizione di Monad
Dato che Monads (come Option<'T>
o List<'T>
) sono comunemente usati nella programmazione funzionale ci sono anche operatori comuni ma meno conosciuti per comporre funzioni che funzionano con Monads come >>=
, >=>
, <|>
e <*>
.
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
Conclusione
Per i nuovi programmatori funzionali la composizione delle funzioni utilizzando gli operatori potrebbe sembrare opaca e oscura, ma è perché il significato di questi operatori non è comunemente noto come operatori che lavorano sui valori. Tuttavia, con un po 'di allenamento usando |>
, >>
, >>=
e >=>
diventa naturale come usare +
, -
, *
e /
.
Latebinding in F # usando? operatore
In un linguaggio tipizzato staticamente come F#
lavoriamo con tipi ben noti in fase di compilazione. Consumiamo fonti di dati esterne in un modo sicuro per tipo utilizzando i provider di tipi.
Tuttavia, occasionalmente è necessario utilizzare l'associazione tardiva (come la dynamic
in C#
). Ad esempio quando si lavora con documenti JSON
che non hanno uno schema ben definito.
Per semplificare il lavoro con l'associazione tardiva, F#
fornisce supporto agli operatori di ricerca dinamica ?
e ?<-
.
Esempio:
// (?) 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
Si scopre che il supporto F#
per l'associazione tardiva è semplice ma flessibile.