Számítási kifejezések
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 "kiszivárogtathatja" a számításokat egy adott környezeten kívül, absztrakciók nélkül, hogy megakadályozza ezt. 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 szerkesztő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ő űrlapot tartalmazza:
builder-expr { cexper }
Ebben a formában egy olyan szerkesztőtípus neve, builder-expr
amely meghatározza a számítási kifejezést, és cexper
a számítási kifejezés 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 a szintaktikus cukor használata let!
után egy minta egyezés az eredmény.
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 let
kö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!
.
let!
a szerkesztőtípus tagja határozza Bind(x, f)
meg.
and!
A and!
kulcsszó lehetővé teszi, hogy több számítási kifejezés hívásának eredményét performanszos módon kösse össze.
let doThingsAsync url =
async {
let! data = getDataAsync url
and! moreData = getMoreDataAsync anotherUrl
and! evenMoreData = getEvenMoreDataAsync someUrl
...
}
A drága kötések újrafuttatása erők sorozatával let! ... let! ...
, ezért a használatot let! ... and! ...
számos számítási kifejezés eredményeinek kötésekor kell használni.
and!
elsősorban a MergeSources(x1, x2)
szerkesztőtípushoz tartozó tag határozza meg.
Megadható úgy is, MergeSourcesN(x1, x2 ..., xN)
hogy csökkentse a csonkoló csomópontok számát, és BindN(x1, x2 ..., xN, f)
meg is határozható a BindNReturn(x1, x2, ..., xN, f)
számítási kifejezések eredményeinek hatékony kötéséhez csomópontok hozzáadása nélkül.
do!
A do!
kulcsszó egy olyan számítási kifejezés meghívására használható, amely -szerű típust unit
ad vissza (amelyet a szerkesztő tagja Zero
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!
a szerkesztőtípus tagja határozza Bind(x, f)
meg, ahol f
egy unit
.
yield
A yield
kulcsszó arra szolgál, hogy visszaadjon egy értéket a számítási kifejezésből, hogy az felhasználható legyen IEnumerable<T>:
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 kihagyás yield
leggyakoribb módja az operátorral ->
:
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 szerkesztőtípus tagja határozza Yield(x)
meg, ahol x
az elem visszahozandó.
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
A kiértékeléskor a függvény által yield!
hívott számítási kifejezés elemei egyenként lesznek visszaadva, és összesimítják az eredményt.
yield!
a szerkesztőtípus tagja határozza YieldFrom(x)
meg, ahol 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 számítási kifejezések használatán yield
kívül egy számítási kifejezés "kiegészíté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 szerkesztőtípus tagja határozza Return(x)
meg, ahol x
az elem körbefut. A let! ... return
használathoz BindReturn(x, f)
jobb teljesítmény érhető el.
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!
a szerkesztőtípus tagja határozza ReturnFrom(x)
meg, 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 -> ...
}
Számítási kifejezés meghívásakor match!
a függvény a következő hívás let!
eredményét fogja felismerni: . 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> |
Számítási kifejezések meghívása let! és do! megadása. |
BindN |
(M<'T1> * M<'T2> * ... * M<'TN> * ('T1 * 'T2 ... * 'TN -> M<'U>)) -> M<'U> |
Hatékony let! és and! számítási kifejezésekre van hívva a bemenetek egyesítése nélkül.pl. 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. |
BindReturn |
(M<'T1> * ('T1 -> 'T2)) -> M<'T2> |
Hatékony let! ... return számítási kifejezésekre van hívva. |
BindNReturn |
(M<'T1> * M<'T2> * ... * M<'TN> * ('T1 * 'T2 ... * 'TN -> M<'U>)) -> M<'U> |
let! ... and! ... return A számítási kifejezések hatékony egyesítését követeli meg a bemenetek egyesítése nélkül.pl. 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 való meghívás and! , de a kihasználatlan csomópontok számának csökkentésével javítja a hatékonyságot.pl. 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>> |
A számítási kifejezések kifejezéseinek meghívása for...do . |
TryFinally |
Delayed<'T> * (unit -> unit) -> M<'T> |
A számítási kifejezések kifejezéseinek meghívása try...finally . |
TryWith |
Delayed<'T> * (exn -> M<'T>) -> M<'T> |
A számítási kifejezések kifejezéseinek meghívása try...with . |
Using |
'T * ('T -> M<'U>) -> M<'U> when 'T :> IDisposable |
A számítási kifejezések kötéseinek meghívása use . |
While |
(unit -> bool) * Delayed<'T> -> M<'T> Vagy(unit -> bool) * Delayed<unit> -> M<unit> |
A számítási kifejezések kifejezéseinek meghívása while...do . |
Yield |
'T -> M<'T> |
A számítási kifejezések kifejezéseinek meghívása yield . |
YieldFrom |
M<'T> -> M<'T> |
A számítási kifejezések kifejezéseinek meghívása yield! . |
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 idézőjelekké fordítja le. |
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 függvények visszatérési Delay
típusa, következésképpen a paraméter. 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észletesebb megjelenésért lásd itt .
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 hívásokat Run
Delay
a rendszer kihagyja, ha 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.
Expression | 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. A szerkesztőosztálynak nem kell implementálnia az összes metódust, és támogatnia kell 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 kulcsszót a use
számítási kifejezésekben, kihagyhatja a szerkesztőosztály definícióját Use
.
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 OkOrException
eddig kiértékelt kifejezés hibaállapotát kódolja. Ez a kód számos tipikus mintát mutat be, amelyeket a számítási kifejezésekben használhat, például a szerkesztő metódusok egy részének kazántáblás implementációit.
/// 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 a következő 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 szerkesztő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 elején lép a hatókörbe. Ezért ne használjon olyan azonosítókat, amelyek neve megegyezik a blokk egyéni műveletével. Kerülje például az olyan azonosítók használatát, mint a all
lekérdezési kifejezések vagy last
a lekérdezési kifejezések.
Meglévő szerkesztő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 kiterjesztését mutatja be.
open System
open FSharp.Linq
type QueryBuilder with
[<CustomOperation("existsNot")>]
member _.ExistsNot (source: QuerySource<'T, 'Q>, predicate) =
System.Linq.Enumerable.Any (source.Source, Func<_,_>(predicate)) |> not
Az egyéni 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.