Skip to content
Learni
View all tutorials
Langages de programmation

How to Master F# for .NET Applications in 2026

Lire en français

Introduction

F# is a first-class functional language on the .NET platform that combines the power of functional programming with the Microsoft ecosystem. In 2026, it excels in data engineering, web services, and concurrent applications thanks to its robust type system and computation expressions. This intermediate tutorial guides you step by step to build a complete console application leveraging discriminated unions, computation expressions, and async workflows. Each concept is illustrated with functional code that you can run immediately.

Prerequisites

  • .NET SDK 8.0 or higher
  • Visual Studio 2022 or VS Code with the Ionide extension
  • Basic knowledge of functional programming and TypeScript/C#
  • Familiarity with pattern matching concepts

Initialize the F# Project

terminal
dotnet new console -lang F# -o FSharpTutorial
cd FSharpTutorial
dotnet run

This command creates a standard F# console project. The Program.fs file already contains a main entry point. Run dotnet run to verify that the environment works correctly.

Define Discriminated Unions

Domain.fs
module Domain

type OrderStatus =
    | Pending
    | Shipped of DateTime
    | Delivered of DateTime * string

let status = Shipped (DateTime.Now)

let describe status =
    match status with
    | Pending -> "En attente"
    | Shipped date -> $"Expédié le {date}"
    | Delivered (date, carrier) -> $"Livré par {carrier} le {date}"

Discriminated unions model exhaustive states safely. Pattern matching forces the developer to handle all cases, avoiding common runtime errors found in C#.

Use Immutable Records

Models.fs
module Models

type Order = {
    Id: Guid
    Customer: string
    Amount: decimal
    Status: Domain.OrderStatus
}

let createOrder customer amount =
    { Id = Guid.NewGuid()
      Customer = customer
      Amount = amount
      Status = Domain.Pending }

F# records are immutable by default. This guarantees data safety in concurrent applications and makes reasoning about code easier.

Implement an Async Workflow

AsyncWorkflow.fs
module AsyncWorkflow

open System.Net.Http
open System.Threading.Tasks

let fetchDataAsync (url: string) =
    async {
        use client = new HttpClient()
        let! response = client.GetStringAsync(url) |> Async.AwaitTask
        return response
    }

let runExample () =
    fetchDataAsync "https://jsonplaceholder.typicode.com/todos/1"
    |> Async.RunSynchronously
    |> printfn "%s"

Async workflows in F# simplify asynchronous programming without callbacks. The async keyword creates a computation expression that automatically handles continuations.

Create a Computation Expression

ResultBuilder.fs
module ResultBuilder

type ResultBuilder() =
    member _.Bind(x, f) =
        match x with
        | Ok v -> f v
        | Error e -> Error e
    member _.Return(x) = Ok x

let result = ResultBuilder()

let validateOrder order =
    result {
        let! amount = if order.Amount > 0m then Ok order.Amount else Error "Montant invalide"
        return { order with Amount = amount }
    }

Computation expressions allow creating DSLs for handling errors or state. Here, the Result builder avoids nested if-else statements and makes the code more readable.

Main Entry Point

Program.fs
open Domain
open Models
open AsyncWorkflow
open ResultBuilder

[<EntryPoint>]
let main argv =
    let order = createOrder "Alice" 150.0m
    printfn "%s" (describe order.Status)
    let validated = validateOrder order
    match validated with
    | Ok o -> printfn "Commande valide: %A" o
    | Error msg -> printfn "Erreur: %s" msg
    runExample () |> ignore
    0

The Program.fs file orchestrates all previous modules. It demonstrates the combined use of discriminated unions, records, and computation expressions in a real workflow.

Best Practices

  • Always prefer immutable types for business data
  • Use discriminated unions to model exhaustive states
  • Leverage computation expressions for complex operations (async, result, option)
  • Write tests with FsUnit or Expecto from the start
  • Treat compiler warnings as errors for safer code

Common Errors to Avoid

  • Forgetting to handle all cases in pattern matching (the compiler will warn you)
  • Using mutable types in concurrent code
  • Ignoring performance implications of infinite sequences with Seq
  • Mixing F# and C# code without understanding naming conventions

Further Reading

Deepen your knowledge of Type Providers and F# metaprogramming in our Learni training courses.