Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Az F# számítási kifejezései kényelmes szintaxist biztosítanak a számítások írásához, amelyek vezérlőfolyamat-szerkezetek és kötések használatával sorrendbe állíthatók és kombinálhatók. A számítási kifejezés típusától függően a monádok, monoidok, monad transzformátorok és applikatív funktorok kifejezésére is gondolhatunk. Más nyelvektől (például a Haskell do-notationtól ) eltérően azonban nem egyetlen absztrakcióhoz vannak kötve, és nem támaszkodnak makrókra vagy más metaprogramozási formákra a kényelmes és környezetfüggő szintaxis eléréséhez.
Áttekintés
A számítások számos formában használhatók. A számítás leggyakoribb formája az egyszálas végrehajtás, amely könnyen érthető és módosítható. Azonban nem minden számítási forma olyan egyszerű, mint az egyszálas végrehajtás. Néhány példa:
- Nem determinisztikus számítások
- Aszinkron számítások
- Effektusos számítások
- Generatív számítások
Általánosságban elmondható, hogy vannak környezetfüggő számítások, amelyeket az alkalmazás bizonyos részeiben kell elvégeznie. A környezetfüggő kód írása nehézkes lehet, mivel könnyen kihagyhatja a számításokat egy adott környezeten kívül, ha nincsenek meg a szükséges absztrakciók ennek megakadályozására. Ezeket az absztrakciókat gyakran nehéz önállóan írni, ezért az F#-nak általános módja van az úgynevezett számítási kifejezések elvégzésére.
A számítási kifejezések egységes szintaxist és absztrakciós modellt kínálnak a környezetfüggő számítások kódolásához.
Minden számítási kifejezést egy építőtípus biztosít. A szerkesztő típusa határozza meg a számítási kifejezéshez elérhető műveleteket. Lásd: Új típusú számítási kifejezés létrehozása, amely bemutatja, hogyan hozhat létre egyéni számítási kifejezéseket.
Szintaxis áttekintése
Minden számítási kifejezés a következő formát ölti:
builder-expr { cexper }
Ebben a formában builder-expr egy építő típus neve, amely meghatározza a számítási kifejezést, és cexper a számítási kifejezéstörzse. A számítási kifejezés kódja például async így nézhet ki:
let fetchAndDownload url =
async {
let! data = downloadData url
let processedData = processData data
return processedData
}
Egy számítási kifejezésben egy speciális, további szintaxis érhető el, ahogyan az előző példában is látható. Számítási kifejezésekkel a következő kifejezésűrlapok használhatók:
expr { let! ... }
expr { and! ... }
expr { do! ... }
expr { yield ... }
expr { yield! ... }
expr { return ... }
expr { return! ... }
expr { match! ... }
Ezek a kulcsszavak és egyéb standard F#-kulcsszavak csak akkor érhetők el számítási kifejezésekben, ha a háttérszerkesztő típusában vannak definiálva. Az egyetlen kivétel ez alól az match!, ami maga szintaktikus cukor, mivel a let! használatát követő minta egyezést egyszerűsíti.
A szerkesztő típusa egy olyan objektum, amely speciális metódusokat határoz meg, amelyek szabályozzák a számítási kifejezés töredékeinek kombinálását; vagyis a metódusok szabályozzák a számítási kifejezés viselkedését. A szerkesztőosztály leírásának másik módja, ha azt mondjuk, hogy lehetővé teszi számos F#-szerkezet, például hurkok és kötések működésének testreszabását.
let!
A let! kulcsszó egy hívás eredményét egy másik számítási kifejezéshez köti egy névhez:
let doThingsAsync url =
async {
let! data = getDataAsync url
...
}
Ha a hívást egy számítási kifejezéshez letköti, akkor nem fogja megkapni a számítási kifejezés eredményét. Ehelyett a nem realizált hívás értékét az adott számítási kifejezéshez kötötte. Az eredményhez való kötéshez használható let! .
A let!-t a szerkesztőtípus Bind(x, f) tagja határozza meg.
and!
A and! kulcsszóval hatékonyabban kötheti össze több számítási kifejezés hívásának eredményét. Ez a kulcsszó lehetővé teszi az alkalmazásalapú számítási kifejezéseket, amelyek a standard monadikus megközelítéstől eltérő számítási modellt biztosítanak.
let doThingsAsync url =
async {
let! data = getDataAsync url
and! moreData = getMoreDataAsync anotherUrl
and! evenMoreData = getEvenMoreDataAsync someUrl
...
}
A számítások sorozatának let! ... let! ... használatával egymás után hajtja végre a számításokat, még akkor is, ha függetlenek. Ezzel szemben azt jelzi, let! ... and! ... hogy a számítások függetlenek, lehetővé téve az alkalmazásalapú kombinációt. Ez a függetlenség lehetővé teszi a számítási kifejezések szerzői számára a következő műveletek elvégzését:
- A számítások hatékonyabb végrehajtása.
- A számítások párhuzamosan is futtathatók.
- Az eredmények halmozása szükségtelen szekvenciális függőségek nélkül.
A korlátozás az, hogy a számítások kombinálása and! nem függhet a korábban kötött értékek eredményeitől ugyanazon a láncon let!/and! belül. Ez a kompromisszum lehetővé teszi a teljesítménybeli előnyöket.
and! elsősorban a MergeSources(x1, x2) szerkesztőtípushoz tartozó tag határozza meg.
Opcionálisan, MergeSourcesN(x1, x2 ..., xN) meghatározható a tuplázó csomópontok számának csökkentésére, és BindN(x1, x2 ..., xN, f), illetve BindNReturn(x1, x2, ..., xN, f) is meghatározható a számítási kifejezések eredményeinek hatékony kötésére, tuplázó csomópontok nélkül.
Az alkalmazásalapú számítási kifejezésekről további információt az F# 5 és az F# RFC FS-1063applicative Computation Expressions című témakörben talál.
do!
A do! kulcsszó egy olyan számítási kifejezés hívásához használható, amely unit-szerű típust ad vissza (amit az építő Zero tagja határoz meg).
let doThingsAsync data url =
async {
do! submitData data url
...
}
Az aszinkron munkafolyamat esetében ez a típus .Async<unit> Más számítási kifejezések esetében a típus valószínűleg az .CExpType<unit>
do!-t a szerkesztőtípus Bind(x, f) tagja határozza meg, ahol a f egy unit-t eredményez.
yield
A yield kulcsszó arra szolgál, hogy visszaadjon egy értéket a számítási kifejezésből, így az IEnumerable<T>-ként felhasználható:
let squares =
seq {
for i in 1..10 do
yield i * i
}
for sq in squares do
printfn $"%d{sq}"
A legtöbb esetben a hívók kihagyhatják. A yield kihagyásának leggyakoribb módja a -> operátor:
let squares =
seq {
for i in 1..10 -> i * i
}
for sq in squares do
printfn $"%d{sq}"
Összetettebb kifejezések esetén, amelyek számos különböző értéket eredményeznek, és talán feltételesen is, egyszerűen kihagyják a kulcsszót:
let weekdays includeWeekend =
seq {
"Monday"
"Tuesday"
"Wednesday"
"Thursday"
"Friday"
if includeWeekend then
"Saturday"
"Sunday"
}
A C#-ban használt hozam kulcsszóhoz hasonlóan a számítási kifejezés minden eleme vissza lesz engedett az iteráció során.
yield a megépítő típusának Yield(x) tagja határozza meg, ahol x az az elem, amit vissza kell adni.
yield!
A yield! kulcsszó egy számítási kifejezésből származó értékek gyűjteményének összesimítására használható:
let squares =
seq {
for i in 1..3 -> i * i
}
let cubes =
seq {
for i in 1..3 -> i * i * i
}
let squaresAndCubes =
seq {
yield! squares
yield! cubes
}
printfn $"{squaresAndCubes}" // Prints - 1; 4; 9; 1; 8; 27
Kiértékeléskor a yield! által hívott számítási kifejezés elemei egyenként lesznek visszaadva, laposítva az eredményt.
yield! a szerkesztőtípus YieldFrom(x) tagja határozza meg, ahol a x értékek gyűjteménye van.
Eltérően yield, yield! explicit módon kell megadni. Viselkedése nem implicit a számítási kifejezésekben.
return
A return kulcsszó a számítási kifejezésnek megfelelő típusba csomagolja az értéket. A yield-t használó számítási kifejezéseken kívül, egy számítási kifejezés "befejezésére" is használható.
let req = // 'req' is of type 'Async<data>'
async {
let! data = fetch url
return data
}
// 'result' is of type 'data'
let result = Async.RunSynchronously req
return a Return(x) tag határozza meg az építő típusban, ahol x az a körbecsomagolandó elem.
let! ... return használatához a BindReturn(x, f) jobb teljesítményt nyújthat.
return!
A return! kulcsszó felismeri egy számítási kifejezés értékét, és olyan sortöréseket végez, amelyek a számítási kifejezésnek megfelelő típust eredményezik:
let req = // 'req' is of type 'Async<data>'
async {
return! fetch url
}
// 'result' is of type 'data'
let result = Async.RunSynchronously req
return!-t a ReturnFrom(x) tag határozza meg a szerkesztőtípuson, ahol x egy másik számítási kifejezés.
match!
A match! kulcsszó lehetővé teszi egy másik számítási kifejezés és mintaegyeztetés meghívásának beírását az eredmény alapján:
let doThingsAsync url =
async {
match! callService url with
| Some data -> ...
| None -> ...
}
Amikor egy számítási kifejezést hív meg a match! segítéségével, az a hívás eredményét let! szerint fogja végrehajtani. Ezt gyakran használják olyan számítási kifejezések meghívásakor, ahol az eredmény nem kötelező.
Beépített számítási kifejezések
Az F#-magtár négy beépített számítási kifejezést határoz meg: Szekvenciakifejezéseket, Aszinkron kifejezéseket, Feladatkifejezéseket és Lekérdezési kifejezéseket.
Új típusú számítási kifejezés létrehozása
A saját számítási kifejezések jellemzőinek meghatározásához hozzon létre egy szerkesztőosztályt, és definiáljon bizonyos speciális metódusokat az osztályban. A szerkesztőosztály opcionálisan definiálhatja a metódusokat az alábbi táblázatban felsoroltak szerint.
Az alábbi táblázat a munkafolyamat-szerkesztő osztályban használható módszereket ismerteti.
| Módszer | Jellemző aláírás(ok) | Leírás |
|---|---|---|
Bind |
M<'T> * ('T -> M<'U>) -> M<'U> |
Hívás történt let!-ra és do!-re a számítási kifejezésekben. |
BindN |
(M<'T1> * M<'T2> * ... * M<'TN> * ('T1 * 'T2 ... * 'TN -> M<'U>)) -> M<'U> |
Hatékony let! és and! számítási kifejezések alkalmazására van szükség a bemenetek egyesítése nélkül.például : Bind3. Bind4 |
Delay |
(unit -> M<'T>) -> Delayed<'T> |
Függvényként körbefuttat egy számítási kifejezést.
Delayed<'T> lehet bármilyen típusú, gyakran M<'T> használt vagy unit -> M<'T> használt. Az alapértelmezett implementáció egy M<'T>. |
Return |
'T -> M<'T> |
Számítási kifejezésekben hívjuk return meg. |
ReturnFrom |
M<'T> -> M<'T> |
Számítási kifejezésekben hívjuk return! meg. |
ReturnFromFinal |
M<'T> -> M<'T> |
Ha van ilyen, hívja meg return! és do! mikor a tail-call pozícióban. |
BindReturn |
(M<'T1> * ('T1 -> 'T2)) -> M<'T2> |
Hatékony let! ... return szükség van a számítási kifejezésekben. |
BindNReturn |
(M<'T1> * M<'T2> * ... * M<'TN> * ('T1 * 'T2 ... * 'TN -> M<'U>)) -> M<'U> |
Hatékony let! ... and! ... return használata a számítási kifejezésekben a bemenetek összevonása nélkül.például : Bind3Return. Bind4Return |
MergeSources |
(M<'T1> * M<'T2>) -> M<'T1 * 'T2> |
Számítási kifejezésekben hívjuk and! meg. |
MergeSourcesN |
(M<'T1> * M<'T2> * ... * M<'TN>) -> M<'T1 * 'T2 * ... * 'TN> |
Számítási kifejezésekben hívják meg and!, ami a tuplasítócsonópontok számának csökkentésével javítja a hatékonyságot.például : MergeSources3. MergeSources4 |
Run |
Delayed<'T> -> M<'T> vagyM<'T> -> 'T |
Számítási kifejezést hajt végre. |
Combine |
M<'T> * Delayed<'T> -> M<'T> vagyM<unit> * M<'T> -> M<'T> |
A számítási kifejezésekben történő szekvenálást kéri. |
For |
seq<'T> * ('T -> M<'U>) -> M<'U> vagyseq<'T> * ('T -> M<'U>) -> seq<M<'U>> |
Meghívás for...do kifejezések számítási kifejezésekben. |
TryFinally |
Delayed<'T> * (unit -> unit) -> M<'T> |
Meghívás try...finally kifejezések számítási kifejezésekben. |
TryWith |
Delayed<'T> * (exn -> M<'T>) -> M<'T> |
Meghívás try...with kifejezések számítási kifejezésekben. |
Using |
'T * ('T -> M<'U>) -> M<'U> when 'T :> IDisposable |
A számítási kifejezésekben szükséges kötéseket use hívták meg. |
While |
(unit -> bool) * Delayed<'T> -> M<'T>vagy(unit -> bool) * Delayed<unit> -> M<unit> |
Meghívás while...do kifejezések számítási kifejezésekben. |
Yield |
'T -> M<'T> |
Meghívás yield kifejezések számítási kifejezésekben. |
YieldFrom |
M<'T> -> M<'T> |
Meghívás yield! kifejezések számítási kifejezésekben. |
YieldFromFinal |
M<'T> -> M<'T> |
Ha jelen van, akkor a yield! tail-call helyzetben, do! illetve a tail-call helyzetben a tartalék ReturnFromFinal |
Zero |
unit -> M<'T> |
A számítási kifejezésekben lévő kifejezések üres else ágainak if...then meghívása. |
Quote |
Quotations.Expr<'T> -> Quotations.Expr<'T> |
Azt jelzi, hogy a számítási kifejezés idézőjelként lesz átadva a Run tagnak. A számítás összes példányát egy kifejezéssé alakítja. |
A szerkesztőosztály számos metódusa használ és ad vissza egy M<'T> szerkezetet, amely általában egy külön definiált típus, amely az egyesítendő számítások típusát jellemzi, például Async<'T> az aszinkron kifejezésekhez és Seq<'T> a szekvencia-munkafolyamatokhoz. Ezeknek a metódusoknak az aláírásai lehetővé teszik, hogy egyesíthetők és egymásba ágyazódjanak, így az egyik szerkezetből visszaadott munkafolyamat-objektum átadható a következőnek.
Számos függvény argumentumként használja az eredményt Delay : Run, While, TryWith, TryFinallyés Combine. A Delayed<'T> típus a Delay visszatérési típusa, és következésképpen ezeknek a függvényeknek a paramétere.
Delayed<'T> tetszőleges típus lehet, amelyet nem kell összekapcsolni M<'T>; gyakran M<'T> vagy (unit -> M<'T>) gyakran használják. Az alapértelmezett implementáció a következő M<'T>: . Részletesebben lásd a típuskorlátozások ismertetését.
A fordító egy számítási kifejezés elemzésekor beágyazott függvényhívások sorozatává fordítja le a kifejezést az előző táblázatban szereplő metódusok és a számítási kifejezés kódjának használatával. A beágyazott kifejezés a következő formában található:
builder.Run(builder.Delay(fun () -> {{ cexpr }}))
A fenti kódban a rendszer kihagyja a Run és Delay hívásokat, ha azok nincsenek definiálva a számítási kifejezéskészítő osztályban. A számítási kifejezés törzse, amely itt a következőképpen van jelölve {{ cexpr }}, a szerkesztőosztály metódusainak további hívásaiba lesz lefordítva. Ez a folyamat rekurzív módon van definiálva az alábbi táblázatban található fordításoknak megfelelően. A két zárójelen {{ ... }} belüli kód továbbra is lefordítandó, egy F#-kifejezést jelöl, expr és cexpr egy számítási kifejezést jelöl.
| Kifejezés | Fordítás |
|---|---|
{{ let binding in cexpr }} |
let binding in {{ cexpr }} |
{{ let! pattern = expr in cexpr }} |
builder.Bind(expr, (fun pattern -> {{ cexpr }})) |
{{ do! expr in cexpr }} |
builder.Bind(expr, (fun () -> {{ cexpr }})) |
{{ yield expr }} |
builder.Yield(expr) |
{{ yield! expr }} |
builder.YieldFrom(expr) |
{{ return expr }} |
builder.Return(expr) |
{{ return! expr }} |
builder.ReturnFrom(expr) |
{{ use pattern = expr in cexpr }} |
builder.Using(expr, (fun pattern -> {{ cexpr }})) |
{{ use! value = expr in cexpr }} |
builder.Bind(expr, (fun value -> builder.Using(value, (fun value -> {{ cexpr }})))) |
{{ if expr then cexpr0 }} |
if expr then {{ cexpr0 }} else builder.Zero() |
{{ if expr then cexpr0 else cexpr1 }} |
if expr then {{ cexpr0 }} else {{ cexpr1 }} |
{{ match expr with | pattern_i -> cexpr_i }} |
match expr with | pattern_i -> {{ cexpr_i }} |
{{ for pattern in enumerable-expr do cexpr }} |
builder.For(enumerable-expr, (fun pattern -> {{ cexpr }})) |
{{ for identifier = expr1 to expr2 do cexpr }} |
builder.For([expr1..expr2], (fun identifier -> {{ cexpr }})) |
{{ while expr do cexpr }} |
builder.While(fun () -> expr, builder.Delay({{ cexpr }})) |
{{ try cexpr with | pattern_i -> expr_i }} |
builder.TryWith(builder.Delay({{ cexpr }}), (fun value -> match value with | pattern_i -> expr_i | exn -> System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(exn).Throw())) |
{{ try cexpr finally expr }} |
builder.TryFinally(builder.Delay({{ cexpr }}), (fun () -> expr)) |
{{ cexpr1; cexpr2 }} |
builder.Combine({{ cexpr1 }}, {{ cexpr2 }}) |
{{ other-expr; cexpr }} |
expr; {{ cexpr }} |
{{ other-expr }} |
expr; builder.Zero() |
Az előző táblázatban egy olyan kifejezést ismertet, other-expr amely egyébként nem szerepel a táblázatban. Az építőosztálynak nem kell implementálnia az összes metódust, és nem szükséges támogatnia az előző táblázatban felsorolt összes fordítást. Ezek a nem implementált szerkezetek nem érhetők el az ilyen típusú számítási kifejezésekben. Ha például nem szeretné támogatni a use kulcsszót a számítási kifejezésekben, kihagyhatja az Use építőosztály definícióját.
Az alábbi példakód egy számítási kifejezést mutat be, amely lépéssorozatként foglalja magában a számítást, amely egyszerre egy lépésben értékelhető ki. A diszkriminált egyesítő típus az OkOrExceptioneddig kiértékelt kifejezés hibaállapotát kódolja. Ez a kód számos tipikus mintát mutat be, amelyeket a kalkulációs kifejezésekben használhat, például néhány építő metódus sablon implementációját.
/// Represents computations that can be run step by step
type Eventually<'T> =
| Done of 'T
| NotYetDone of (unit -> Eventually<'T>)
module Eventually =
/// Bind a computation using 'func'.
let rec bind func expr =
match expr with
| Done value -> func value
| NotYetDone work -> NotYetDone (fun () -> bind func (work()))
/// Return the final value
let result value = Done value
/// The catch for the computations. Stitch try/with throughout
/// the computation, and return the overall result as an OkOrException.
let rec catch expr =
match expr with
| Done value -> result (Ok value)
| NotYetDone work ->
NotYetDone (fun () ->
let res = try Ok(work()) with | exn -> Error exn
match res with
| Ok cont -> catch cont // note, a tailcall
| Error exn -> result (Error exn))
/// The delay operator.
let delay func = NotYetDone (fun () -> func())
/// The stepping action for the computations.
let step expr =
match expr with
| Done _ -> expr
| NotYetDone func -> func ()
/// The tryFinally operator.
/// This is boilerplate in terms of "result", "catch", and "bind".
let tryFinally expr compensation =
catch (expr)
|> bind (fun res ->
compensation();
match res with
| Ok value -> result value
| Error exn -> raise exn)
/// The tryWith operator.
/// This is boilerplate in terms of "result", "catch", and "bind".
let tryWith exn handler =
catch exn
|> bind (function Ok value -> result value | Error exn -> handler exn)
/// The whileLoop operator.
/// This is boilerplate in terms of "result" and "bind".
let rec whileLoop pred body =
if pred() then body |> bind (fun _ -> whileLoop pred body)
else result ()
/// The sequential composition operator.
/// This is boilerplate in terms of "result" and "bind".
let combine expr1 expr2 =
expr1 |> bind (fun () -> expr2)
/// The using operator.
/// This is boilerplate in terms of "tryFinally" and "Dispose".
let using (resource: #System.IDisposable) func =
tryFinally (func resource) (fun () -> resource.Dispose())
/// The forLoop operator.
/// This is boilerplate in terms of "catch", "result", and "bind".
let forLoop (collection:seq<_>) func =
let ie = collection.GetEnumerator()
tryFinally
(whileLoop
(fun () -> ie.MoveNext())
(delay (fun () -> let value = ie.Current in func value)))
(fun () -> ie.Dispose())
/// The builder class.
type EventuallyBuilder() =
member x.Bind(comp, func) = Eventually.bind func comp
member x.Return(value) = Eventually.result value
member x.ReturnFrom(value) = value
member x.Combine(expr1, expr2) = Eventually.combine expr1 expr2
member x.Delay(func) = Eventually.delay func
member x.Zero() = Eventually.result ()
member x.TryWith(expr, handler) = Eventually.tryWith expr handler
member x.TryFinally(expr, compensation) = Eventually.tryFinally expr compensation
member x.For(coll:seq<_>, func) = Eventually.forLoop coll func
member x.Using(resource, expr) = Eventually.using resource expr
let eventually = new EventuallyBuilder()
let comp =
eventually {
for x in 1..2 do
printfn $" x = %d{x}"
return 3 + 4
}
/// Try the remaining lines in F# interactive to see how this
/// computation expression works in practice.
let step x = Eventually.step x
// returns "NotYetDone <closure>"
comp |> step
// prints "x = 1"
// returns "NotYetDone <closure>"
comp |> step |> step
// prints "x = 1"
// prints "x = 2"
// returns "Done 7"
comp |> step |> step |> step |> step
A számítási kifejezéseknek van egy mögöttes típusa, amelyet a kifejezés ad vissza. Az alapul szolgáló típus jelentheti a kiszámított eredményt vagy a késleltetett számítást, amely végrehajtható, vagy valamilyen típusú gyűjteményen keresztüli iterálást is lehetővé tehet. Az előző példában a mögöttes típus a következő volt Eventually<_>: . Szekvenciakifejezés esetén a mögöttes típus a következő System.Collections.Generic.IEnumerable<T>: . Lekérdezési kifejezés esetén a mögöttes típus System.Linq.IQueryable. Aszinkron kifejezés esetén a mögöttes típus a .Async Az Async objektum az eredmény kiszámításához végrehajtandó munkát jelöli. Például egy számítás végrehajtására hív Async.RunSynchronously meg, és visszaadja az eredményt.
Egyéni műveletek
Egy számítási kifejezésen egyéni műveletet definiálhat, és egyéni műveletet használhat operátorként egy számítási kifejezésben. Egy lekérdezési operátort például belefoglalhat egy lekérdezési kifejezésbe. Egyéni művelet definiálásakor meg kell határoznia a Hozam és a For metódust a számítási kifejezésben. Egyéni művelet definiálásához tegye azt egy építő osztályba a számítási kifejezéshez, majd alkalmazza a CustomOperationAttribute. Ez az attribútum argumentumként egy sztringet vesz fel, amely egy egyéni műveletben használandó név. Ez a név a számítási kifejezés nyitó kapcsos zárójelének elénél lép be a hatókörbe. Ezért ne használjon olyan azonosítókat, amelyek neve megegyezik ebben a blokkban található egyedi művelettel. Kerülje például az olyan azonosítók használatát lekérdezési kifejezésekben, mint a all vagy last.
A meglévő építők kiterjesztése új egyéni műveletekkel
Ha már rendelkezik szerkesztőosztálysal, az egyéni műveletei kiterjeszthetők ezen a szerkesztőosztályon kívülről is. A bővítményeket modulokban kell deklarálni. A névterek nem tartalmazhatnak bővítménytagokat, kivéve ugyanabban a fájlban és ugyanabban a névtérdeklarációs csoportban, amelyben a típus definiálva van.
Az alábbi példa a meglévő FSharp.Linq.QueryBuilder osztály bővítményeit mutatja be.
open System
open FSharp.Linq
type QueryBuilder with
[<CustomOperation>]
member _.any (source: QuerySource<'T, 'Q>, predicate) =
System.Linq.Enumerable.Any (source.Source, Func<_,_>(predicate))
[<CustomOperation("singleSafe")>] // you can specify your own operation name in the constructor
member _.singleOrDefault (source: QuerySource<'T, 'Q>, predicate) =
System.Linq.Enumerable.SingleOrDefault (source.Source, Func<_,_>(predicate))
Egyedi műveletek túlterhelhetők. További információ: F# RFC FS-1056 – Egyéni kulcsszavak túlterhelésének engedélyezése számítási kifejezésekben.
Számítási kifejezések hatékony összeállítása
A végrehajtást felfüggesztő F#-számítási kifejezések rendkívül hatékony állapotú gépekre fordíthatók az újra felhasználható kód nevű alacsony szintű funkció gondos használatával. Az újra felhasználható kód az F# RFC FS-1087-ben van dokumentálva, és feladatkifejezésekhez használatos.
A szinkron (azaz a végrehajtást nem felfüggesztő) F# számítási kifejezések a hatékony állapotú gépekre fordíthatók úgy, hogy beágyazott függvényeket használnak, beleértve az InlineIfLambda attribútumot is. Ilyenek például az F# RFC FS-1098.
A listakifejezéseket, a tömbkifejezéseket és a szekvenciakifejezéseket az F#-fordító speciálisan kezeli a nagy teljesítményű kód generálásának biztosítása érdekében.