Suche…
Wie man Werte und Funktionen mit gemeinsamen Operatoren zusammenstellt
In der objektorientierten Programmierung besteht die übliche Aufgabe darin, Objekte (Werte) zusammenzustellen. In der funktionalen Programmierung ist es ebenso üblich, Werte und Funktionen zusammenzustellen.
Mit Hilfe von Operatoren wie wir werden Werte von unserer Erfahrung aus anderen Programmiersprachen zu komponieren +
, -
, *
, /
und so weiter.
Wertzusammensetzung
let x = 1 + 2 + 3 * 2
Da die Funktionsprogrammierung sowohl Funktionen als auch Werte enthält, ist es nicht überraschend, dass es für die Funktionszusammenstellung übliche Operatoren wie >>
, <<
, |>
und <|
.
Funktionszusammenstellung
// 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#
Vorwärtsleitung vor der Rückwärtsleitung bevorzugt, weil:
- Typinferenz fließt (im Allgemeinen) von links nach rechts, sodass Werte und Funktionen auch von links nach rechts fließen können
- Weil
<|
und<<
sollte rechte Assoziativität haben, aber inF#
bleiben sie assoziativ, was uns zwingt, einzufügen () - Das Mischen von vorwärts und rückwärts verlaufenden Rohren funktioniert im Allgemeinen nicht, da sie die gleiche Priorität haben.
Monadenzusammensetzung
Da Monaden (wie Option<'T>
oder List<'T>
) häufig in der Funktionsprogrammierung verwendet werden, gibt es auch übliche, aber weniger bekannte Operatoren, die Funktionen erstellen, die mit Monaden arbeiten, wie >>=
, >=>
, <|>
und <*>
.
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
Fazit
Für neue funktionale Programmierer mag die Funktionszusammenstellung mit Operatoren undurchsichtig und unklar sein, aber das liegt daran, dass die Bedeutung dieser Operatoren nicht so allgemein als Operatoren bekannt ist, die mit Werten arbeiten. Mit etwas Training mit |>
wird >>
>>=
und >=>
so selbstverständlich wie mit +
, -
, *
und /
.
Latebinding in F # mit? Operator
In einer statisch typisierten Sprache wie F#
wir mit Typen, die zur Kompilierzeit bekannt sind. Wir konsumieren externe Datenquellen typsicher mit Typanbietern.
Gelegentlich muss jedoch die späte Bindung verwendet werden (z. B. dynamic
in C#
). Zum Beispiel beim Arbeiten mit JSON
Dokumenten, die kein klar definiertes Schema haben.
Um die Arbeit mit Late Binding zu vereinfachen, unterstützt F#
dynamische Suchoperatoren ?
und ?<-
.
Beispiel:
// (?) 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
Es stellt sich heraus, dass die F#
-Unterstützung für spätes Binden einfach und dennoch flexibel ist.