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
dotnet new console -lang F# -o FSharpTutorial
cd FSharpTutorial
dotnet runThis 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
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
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
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
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
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
0The 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.