Frank Hliva - PlutonKrea s.r.o

Front-end Developer & majiteľ

rokov skúsenosti

rokov prax

roky React

Úvod / Blog / F# - async a kompatibilita s jazykom C#

F# - async a kompatibilita s jazykom C#

Publikoval Frank Hliva 2019-12-11 14:38:00 print comments
F# C# async await

Jazyk F# je priekopníkom v zavádzaní nových paradigiem a postupov a tak to bolo aj v prípade práce s asynchrónnym kódom. Zatiaľ čo C# zaviedol async / await až vo verzii 5.0 (jún 2013) F# mal syntaktický cukor pre prácu s asynchrónnym kódom už od verzie 2.0 (rok 2010) ako úplne prvý jazyk od Microsoftu. Postavil ho na computation expressions. Vidieť že Don Syme robí dobrú prácu. Aj keď paradoxne implementácia async v C# sa mi páči výnimočne o máličko viac. Môžme si to hneď aj ukázať na príkladoch:

Ukážka async / await v jazyku C#:

async void printGoogleHtmlToConsole()
{
	var webClient = new System.Net.WebClient();
	var html = await webClient.DownloadStringTaskAsync("https://www.google.com/");
	Console.WriteLine("Google HTML:", html);
}

Ukážka async computation expressions v jazyku F#:

let printGoogleHtmlToConsole () = async {
	let webClient = System.Net.WebClient()
	let! html = webClient.AsyncDownloadString "https://www.google.com/"
	printfn "Google HTML: %s" html }

Ako vidíte v F# sa namiesto await používa keyword let! (let binding s výkričníkom) Jazyk C# si urobil implementáciu async / await po svojom je založená na Task-och presnejšie typy Task<'T> a Task (pre prázdny výsledok).

Ale keďže C# je jazyk pre mainstream a vačšina .NET kódu sa píše v jazyku C# knižníc sa prispôsobuje práve tomuto jazyku. A preto mnoho asynchrónnyeho kódu podporuje podporuje len Tasky a na F# sa zabúda. Taktiež vznikajú duplicity napr.: webClient.DownloadStringTaskAsync pre C# vracajúca výsledok typu Task<string> a webClient.AsyncDownloadString pre F# vracajúca typ Async<string>

Žiaľ F# sa musí držať spatnej kompatibility a preto pracuje s typom Async<'T> nekompatibilným s Task<'T> samozrejme vývojári F# mysleli aj na kompatibilitu s Taskami a napísali nám funkciu Async.AwaitTask ktorá Task<'T> konvertuje na Async<'T> a Task na Async<unit>.

Jej použitie si môžme ukázať na nasledovnom príklade. Dajme tomu že chceme awaitnúť C#-čkovú metódu WebClient.DownloadStringTaskAsync ktorá samozrejme vracia Task<'T>:

let printGoogleHtmlToConsole () = async {
	let webClient = System.Net.WebClient()
	let! html = (webClient.DownloadStringTaskAsync "https://www.google.com/" |> Async.AwaitTask)
	printfn "Google HTML: %s" html }

Aby sme dostali z Tasku Async<'T> zavolali sme konverznú funkciu Async.AwaitTask a potom už s asynchrónnym výsledkom pracujeme klasickým spôsobom ako sa na F# patrí. Že sa vám ten kód nepáči? Ani mne. A keďže som lenivý programátor a nechce sa mi neustále písať Async.AwaitTask kládol som si otázku prečo nemohol Don Syme jednoducho rozšíriť AsyncBuilder na Tasky? A potom ma napadlo, že prečo si ho nerozšíriť sám? Veď jazyk F# je natoľko flexibilný, že sa dá jednoducho rozšíriť o nové prvky jazyka... Urobil som teda rozšírenie AsyncBuildera ktorý do asynchrónnych computation expressions-s, napokon pridáva Tasky aj do F# async. Je to pár riadkov kódu, ktoré má, ale pre nás obrovský prínos.

A tu je pôvodný kód prepísaný po rozšírení AsyncBuildera:

let printGoogleHtmlToConsole () = async {
	let webClient = System.Net.WebClient()
	let! html = webClient.DownloadStringTaskAsync "https://www.google.com/"
	printfn "Google HTML: %s" html }

Wow. Toto rozšírenie som pridal do svojho webového frameworku Deep, takže všetci čo ho používate máte kompatibilitu s taksami by default. A vy ostatní si ho môžete pridať ručne licencia Vám to dovoluje.