Recherche…
Comment composer des valeurs et des fonctions en utilisant des opérateurs communs
En programmation orientée objet, une tâche courante consiste à composer des objets (valeurs). Dans la programmation fonctionnelle, il est courant de composer des valeurs et des fonctions.
Nous sommes habitués à composer des valeurs à partir de notre expérience d'autres langages de programmation en utilisant des opérateurs tels que +
, -
, *
, /
etc.
Composition de la valeur
let x = 1 + 2 + 3 * 2
Comme la programmation fonctionnelle compose à la fois des fonctions et des valeurs, il n'est pas surprenant qu'il existe des opérateurs communs pour la composition de fonctions, tels que >>
, <<
, |>
et <|
.
Composition de fonction
// 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#
, la tuyauterie avant est préférable à la tuyauterie inversée car:
- L'inférence de type (généralement) circule de gauche à droite, il est donc naturel que les valeurs et les fonctions circulent également de gauche à droite
- Parce que
<|
et<<
devraient avoir une associativité correcte mais enF#
ils sont laissés associatifs ce qui nous oblige à insérer () - Mélanger la tuyauterie avant et arrière ne fonctionne généralement pas car ils ont la même priorité.
Composition monade
Comme les monades (comme Option<'T>
ou List<'T>
) sont couramment utilisées dans la programmation fonctionnelle, il existe également des opérateurs courants mais moins connus pour composer des fonctions fonctionnant avec des monades comme >>=
, >=>
, <|>
et <*>
.
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
Conclusion
Pour les nouveaux programmeurs fonctionnels, la composition des fonctions à l'aide d'opérateurs peut sembler opaque et obscure, mais c'est parce que la signification de ces opérateurs n'est pas connue sous le nom d'opérateurs travaillant sur des valeurs. Cependant, avec un entraînement utilisant |>
, >>
, >>=
et >=>
devient aussi naturel que d'utiliser +
, -
, *
et /
.
Reliure tardive en F # en utilisant? opérateur
Dans un langage typé statiquement comme F#
nous travaillons avec des types connus au moment de la compilation. Nous consommons des sources de données externes de manière sécurisée en utilisant des fournisseurs de type.
Cependant, il est parfois nécessaire d'utiliser une liaison tardive (comme la dynamic
en C#
). Par exemple, lorsque vous travaillez avec JSON
documents JSON
qui n'ont pas de schéma bien défini.
Pour simplifier le travail avec une liaison tardive, F#
prend en charge les opérateurs de recherche dynamique ?
et ?<-
.
Exemple:
// (?) 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
Il s'avère que le support F#
pour la liaison tardive est simple mais flexible.