Sök…
Hur man komponerar värden och funktioner med vanliga operatörer
I objektorienterad programmering är en vanlig uppgift att komponera objekt (värden). I funktionell programmering är det lika vanligt att komponera värden och funktioner.
Vi används för att komponera värden från vår erfarenhet av andra programmeringsspråk med operatörer som +
, -
, *
, /
och så vidare.
Värdesammansättning
let x = 1 + 2 + 3 * 2
Eftersom funktionell programmering komponerar funktioner såväl som värden är det inte förvånande att det finns vanliga operatörer för funktionskomposition som >>
, <<
, |>
och <|
.
Funktionssammansättning
// 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')
I F#
föredras rörledningar framför omvänd rörledning eftersom:
- Skriv inferens (vanligtvis) flyter från vänster till höger så det är naturligt att värden och funktioner flyter från vänster till höger också
- Eftersom
<|
och<<
borde ha högerassociativitet men iF#
de vänsterassociativa som tvingar oss att infoga () - Att blanda rör framåt och bakåt fungerar vanligtvis inte eftersom de har samma prioritet.
Monad sammansättning
Eftersom monader (som Option<'T>
eller List<'T>
) vanligtvis används i funktionell programmering finns det också vanliga men mindre kända operatörer att komponera funktioner som arbetar med monader som >>=
, >=>
, <|>
och <*>
.
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
Slutsats
För nya funktionella programmerare kan funktionskomposition som använder operatörer tyckas ogenomskinlig och oklar, men det beror på att betydelsen av dessa operatörer inte är så vanligt känd som operatörer som arbetar med värden. Men med viss träning med hjälp av |>
, >>
, >>=
och >=>
blir det lika naturligt som att använda +
, -
, *
och /
.
Latebinding i F # använder du? operatör
På ett statiskt typspråk som F#
arbetar vi med typer som är välkända vid sammanställningstiden. Vi konsumerar externa datakällor på ett typsäkert sätt med hjälp av typleverantörer.
Ibland finns det emellertid behov av att använda sen bindning (som dynamic
i C#
). Till exempel när du arbetar med JSON
dokument som inte har något väldefinierat schema.
För att förenkla arbetet med sent bindande F#
ger stöd för dynamiska uppslagningsoperatörer ?
och ?<-
.
Exempel:
// (?) 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
Det visar sig att F#
-stödet för sen bindning är enkelt men ändå flexibelt.