F# Training
F# Training
F# Training
  • Presentation
  • Fundamentals
    • Introduction
    • Syntax
      • Bases
      • Functions
      • Rules
      • Exceptions
    • First concepts
    • πŸ”Quiz
  • Functions
    • Signature
    • Concepts
    • Syntax
    • Standard functions
    • Operators
    • Addendum
    • πŸ”Quiz
    • πŸ“œSummary
  • Types
    • Overview
    • Tuples
    • Records
    • Unions
    • Enums
    • Anonymous records
    • Value types
    • πŸ“œRecap
    • Addendum
  • Monadic types
    • Intro
    • Option type
    • Result type
    • Smart constructor
    • πŸš€Computation expression (CE)
    • πŸš€CE theoretical basements
    • πŸ“œRecap
  • Pattern matching
    • Patterns
    • Match expression
    • Active patterns
    • πŸš€Fold function
    • πŸ“œRecap
    • πŸ•ΉοΈExercises
  • Collections
    • Overview
    • Types
    • Common functions
    • Dedicated functions
    • πŸ”Quiz
    • πŸ“œRecap
  • Asynchronous programming
    • Asynchronous workflow
    • Interop with .NET TPL
    • πŸ“œRecap
  • Module and Namespace
    • Overview
    • Namespace
    • Module
    • πŸ”Quiz
    • πŸ“œRecap
  • Object-oriented
    • Introduction
    • Members
    • Type extensions
    • Class, Struct
    • Interface
    • Object expression
    • Recommendations
Powered by GitBook
On this page
  • Interaction with .NET libraries
  • Gateway functions
  • task {} block
  • Async vs Task
  • Recommendation for async function in Fβ™―
  • Pitfalls of the async/await Cβ™― pattern
  • Pitfall 1 - Really asynchronous?
  • Pitfall 2 - Omit the await
  • Async.Parallel
  • Thread-safety

Was this helpful?

Edit on GitHub
  1. Asynchronous programming

Interop with .NET TPL

TPL : Task Parallel Library

Interaction with .NET libraries

Asynchronous libraries in .NET and the async/await Cβ™― pattern: β†’ Based on TPL and the Task type

Gateways with asynchronous worflow Fβ™― :

  • Async.AwaitTask and Async.StartAsTask functions

  • task {} block

Gateway functions

Async.AwaitTask: Task<'T> -> Async<'T> β†’ Consume an asynchronous .NET library in async block

Async.StartAsTask: Async<'T> -> Task<'T> β†’ Launch an async calculation as a Task

let getValueFromLibrary param = async {
    let! value = DotNetLibrary.GetValueAsync param |> Async.AwaitTask
    return value
}

let computationForCaller param =
    async {
        let! result = getAsyncResult param
        return result
    }
    |> Async.StartAsTask

task {} block

Allows to consume an asynchronous .NET library directly, using a single Async.AwaitTask rather than 1 for each async method called.

task {
    use client = new System.Net.Http.HttpClient()
    let! response = client.GetStringAsync("https://www.google.fr/")
    response.Substring(0, 300) |> printfn "%s"
}  // Task<unit>
|> Async.AwaitTask  // Async<unit>
|> Async.RunSynchronously

Async vs Task

1. Calculation start mode

Task = hot tasks β†’ calculations started immediately❗

Async = task generators = calculation specification, independent of startup β†’ Functional approach: no side-effects or mutations, composability β†’ Control of startup mode: when and how πŸ‘

2. Cancellation support

Task: by adding a CancellationToken parameter to async methods β†’ Forces manual testing if token is canceled = tedious + error prone❗

Async: automatic support in calculations - token to be provided at startup πŸ‘

Recommendation for async function in Fβ™―

Cβ™― async applied at a method level β‰  Fβ™― async defines an async block, not an async function

☝ Recommendation: » Put the entire body of the async function in an async block.

// ❌ Avoid
let workThenWait () =
    Thread.Sleep(1000)
    async { do! Async.Sleep(1000) } // Async only in this block 🧐

// βœ… Prefer
let workThenWait () = async {
    Thread.Sleep(1000)
    printfn "work"
    do! Async.Sleep(1000)
}

Pitfalls of the async/await Cβ™― pattern

Pitfall 1 - Really asynchronous?

In Cβ™―: method async remains on the calling thread until the 1st await β†’ Misleading feeling of being asynchronous throughout the method

async Task WorkThenWait() {
    Thread.Sleep(1000); // ⚠️ Blocks calling thread !
    await Task.Delay(1000); // Really async from here πŸ€”
}

Pitfall 2 - Omit the await

async Task PrintAfterOneSecond(string message) {
    await Task.Delay(1000);
    Console.WriteLine($"[{DateTime.Now:T}] {message}");
}

async Task Main() {
    PrintAfterOneSecond("Before"); // ⚠️ Missing `await`β†’ warning CS4014
    Console.WriteLine($"[{DateTime.Now:T}] After");
    await Task.CompletedTask;
}

Compiles but returns unexpected result: After before Before❗

[11:45:27] After
[11:45:28] Before

This pitfall is also present in Fβ™―:

let printAfterOneSecond message = async {
    do! Async.Sleep 1000
    printfn $"[{DateTime.Now:T}] {message}"
}

async {
    printAfterOneSecond "Before" // ⚠️ Missing `do!` β†’ warning FS0020
    printfn $"[{DateTime.Now:T}] After"
} |> Async.RunSynchronously

Compiles but returns another unexpected result: no Before at all ⁉️

[11:45:27] After

Compilation warnings

The previous examples compile but with big warnings!

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the await operator...

Fβ™― warning FS0020 message:

The result of this expression has type Async<unit> and is implicitly ignored. Consider using ignore to discard this value explicitly...

☝ Recommendation: be sure to always handle this type of warnings! This is even more crucial in Fβ™― where compilation can be tricky.

Async.Parallel

Thread-safety

Impure functions can be not thread-safe, for instance if they mutate a shared object like the Console.

It's possible to make them thread-safe using the lock function:

open System
open System.Threading

let printColoredMessage =
    let lockObject = obj ()

    fun (color: ConsoleColor) (message: string) ->
        lock lockObject (fun () ->
            Console.ForegroundColor <- color
            printfn $"%s{message} (thread ID: %i{Thread.CurrentThread.ManagedThreadId})"
            Console.ResetColor())

[ ConsoleColor.Red
  ConsoleColor.Green
  ConsoleColor.Blue ]
|> List.randomShuffle
|> List.indexed
|> List.map (fun (i, color) -> async { printColoredMessage color $"Message {i}" })
|> Async.Parallel
|> Async.RunSynchronously

Results in the console (example):

PreviousAsynchronous workflowNextRecap

Last updated 21 days ago

Was this helpful?

πŸ’‘ Available since Fβ™― 6 (before, we need package nuget)

Cβ™― message:

πŸ”—

Ply
warning CS4014
Lock function documentation