Skip to content

필사 모드: Modern F# 2026 — F# 9 (.NET 9) / Saturn / Giraffe / Bolero / Fable / Feliz / Elmish / Fantomas Deep-Dive Guide

English
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

Prologue — People Still Write F# in 2026

Every year someone asks, "F#? People still use that?"

Yes. And increasingly seriously, inside the .NET world.

- **Jet.com** (now part of Walmart Labs) wrote their pricing engine in F# back in the mid-2010s. After acquisition, core pricing models stayed in F#.

- **Microsoft** itself has compiler teams and Azure services (cost calculation, licensing, analytics) that ship F# code. F# was created at Microsoft Research Cambridge by Don Syme starting in 2005.

- **CitiBank**, **Credit Suisse**, **Standard Chartered**, **Bank of America** — finance keeps a steady seat for F# in pricing and risk models. Domain modeling is strong and side effects are easy to fence in via types.

- **AT&T**, **Demetrix**, **Indaba Music**, **GameSys**, **Tachyus**, **G-Research** — data pipelines, ML, game backends running F#.

- In Japan, **ユーザックシステム (Usac System)** builds RPA and logistics automation with F# and .NET in Tokyo, Osaka, and Nagoya. Microsoft Japan's F# evangelists cite them often.

The enemy of F# in the 2010s wasn't "hard to write." The real enemy was: **"On Mono, non-Windows builds break, Visual Studio for Mac works half the time, and you cannot decide between Paket and NuGet."** What does that look like in 2026?

Answer: **.NET 9 is genuinely cross-platform, F# 9 finished nullable reference types, and Fantomas became the standard formatter.** Almost no one says "it does not build on my laptop" anymore. One `dotnet` CLI runs identically on Windows, macOS, and Linux.

This post is a single-breath tour of the F# stack in 2026: compiler, web frameworks, client side, desktop UI, build tools, data, numerics, ML, SQL, parsers, and the people.

1. Modern F# in 2026 — The .NET 9 Era

A one-page picture first.

Modern F# 2026 Stack

[runtime] .NET 9 (Nov 2024, LTS) — single cross-platform SDK

[compiler] F# 9 (Nov 2024) / F# 10 preview (GitHub main)

[build] dotnet CLI / FAKE / Paket / MSBuild

[web backend] Saturn (Rails-style, opinionated)

Giraffe (functional ASP.NET Core middleware)

Suave (legacy, self-hosted HTTP)

[web client] Fable (F# to JS) + Feliz (React) + Elmish (Elm pattern)

Bolero (Blazor WebAssembly + F#)

[desktop] Avalonia.FuncUI (declarative cross-platform UI)

[tooling] Fantomas (formatter) / Ionide (VS Code) / FsAutoComplete

Polyglot Notebooks (Jupyter on .NET)

[data] FSharp.Data (CSV/JSON/XML/SQL type providers)

Donald (thin SQL client)

[numerics·ML] MathNet.Numerics (linear algebra)

DiffSharp (autodiff, PyTorch-style)

TorchSharp (libtorch bindings) + ML.NET

[concurrency] Hopac (concurrent ML style)

async / Task

[parsers] FParsec (Parsec port)

[cloud] F# in Azure Functions / AWS Lambda

F# in Aspire (distributed .NET workflows)

[production users] Jet.com (Walmart) / Microsoft / CitiBank /

Credit Suisse / GameSys / Tachyus / Usac

That is the 2026 F# landscape. Let us walk every box.

2. F# 9 (Nov 2024, ships with .NET 9) — Nullable + DU Improvements

F# 9 was released in November 2024 together with .NET 9. .NET 9 is a 12-month STS release, but F# 9 itself is bundled with the SDK and rides naturally into the next .NET 10 LTS due November 2025.

There are three core changes.

Nullable reference types completed

C# introduced nullable reference types via `?` in C# 8 (2019). F# always had `Option` for the same purpose, so for years the position was "F# code rarely sees null." But every interop with the BCL (the .NET base class library written in C#) leaked nulls back in.

F# 9 makes **nullable reference types a first-class citizen at the interop boundary**.

// F# 9: nullable annotation

let processName (name: string | null) =

match name with

| null -> "(anonymous)"

| n -> n.ToUpper()

// Safe when calling C# methods

let s: string | null = System.Environment.GetEnvironmentVariable("HOME")

match s with

| null -> printfn "HOME not set"

| home -> printfn "HOME = %s" home

`string | null` maps exactly to C# 9's `string?`. The compiler checks it and warns when you call methods on a value before unwrapping null.

Discriminated union improvements

DUs are the soul of F#. F# 9 improves two things:

1. **Lower boxing cost for struct DUs** — `[<Struct>]` unions stay on the stack more aggressively.

2. **Per-case visibility** — `private` and `internal` can now be applied to individual union cases.

[<Struct>]

type Result<'T, 'E> =

| Ok of value: 'T

| Error of error: 'E

// Visibility per case

type DbHandle =

private | Open of System.Data.IDbConnection

| Closed

Standard library improvements

- `List.randomShuffle`, `Array.randomChoice`, and friends.

- `Result` module additions: `Result.toList`, `Result.toOption`, `Result.toArray`.

- `Seq.tryExactlyOne` returns Some only when the sequence has exactly one element.

let xs = [1; 2; 3]

let shuffled = xs |> List.randomShuffle

// e.g. [2; 1; 3]

let oneOnly = [42] |> Seq.tryExactlyOne

// Some 42

let twoElems = [1; 2] |> Seq.tryExactlyOne

// None

3. F# 10 Preview — Cooking on GitHub

F# 10 is in active development on the `dotnet/fsharp` main branch. As of May 2026, the following candidates sit in RFC.

Shorter `let!` joins in `task`

Combining multiple async results inside `task { ... }` gets long with chained `let!`. F# 10 evaluates an expression-builder extension to compress that.

// F# 9 style

task {

let! a = getUserAsync userId

let! b = getOrdersAsync userId

return (a, b)

}

// F# 10 preview short form under discussion

task {

let! (a, b) = getUserAsync userId, getOrdersAsync userId

return (a, b)

}

Asynchronous type providers

`JsonProvider` and `CsvProvider` from FSharp.Data hit the network at compile time to fetch schemas. On large schemas the IDE can stall. F# 10 plans an async type provider plumbing.

Resumable code improvements

Resumable code, introduced in F# 6 (the basis of `task { }` and `taskSeq { }`), is an internal compiler feature. F# 10 wants to expose it more safely so users can build their own effects, e.g. for Hopac or custom schedulers.

Self-type constraints

To meet C# 11's `static abstract` interface members. Makes SRTPs (Statically Resolved Type Parameters) more natural to express.

F# 10 ships with .NET 10 LTS in November 2025.

4. Saturn — Opinionated Web Framework on Top of Giraffe

Saturn was created by Krzysztof Cieslak (SAFE Stack maintainer, the person behind Ionide). Think of it as **a Rails-style layer sitting on top of Giraffe**.

Core concept

Saturn provides an **application builder DSL**. The ASP.NET Core middleware pipeline gets folded into an F# builder syntax.

open Saturn

let app =

application {

use_router topRouter

url "http://0.0.0.0:8085/"

use_gzip

use_static "public"

use_developer_exceptions

}

run app

That is the entire entry point. Each token inside `application { }` is a function; Saturn defines the `application` computation expression and every keyword (`use_router`, `url`, ...) acts on an `ApplicationBuilder`.

Controller — MVC style

Saturn ships a controller builder too.

let userController = controller {

index (fun ctx -> "Listing all users" |> Controller.text ctx)

show (fun ctx id -> sprintf "Showing user %i" id |> Controller.text ctx)

create (fun ctx -> "Creating new user" |> Controller.text ctx)

edit (fun ctx id -> sprintf "Editing user %i" id |> Controller.text ctx)

delete (fun ctx id -> sprintf "Deleting user %i" id |> Controller.text ctx)

}

let topRouter = router {

forward "/users" userController

}

All seven REST actions in one builder. Rails users find the shape familiar.

SAFE Stack

Saturn rarely travels alone. Usually it ships as part of the **SAFE Stack**.

- **S** — Saturn (server)

- **A** — Azure (hosting)

- **F** — Fable (client, F# to JS)

- **E** — Elmish (Elm pattern)

`dotnet new SAFE` scaffolds a full F# stack project. Client and server share the same F# domain types, which is the killer feature.

5. Giraffe — Functional ASP.NET Core

Giraffe is the lower half of Saturn. It wraps the ASP.NET Core HttpContext pipeline in a functional shape.

HttpHandler

Giraffe's core abstraction is `HttpHandler`.

type HttpHandler = HttpFunc -> HttpContext -> HttpFuncResult

// where HttpFunc = HttpContext -> HttpFuncResult

// HttpFuncResult = Task<HttpContext option>

Take "next handler" and "current context," return a Task that yields the updated context (or None). It corresponds exactly to one slot of ASP.NET Core middleware.

Composition

The `>=>` operator composes handlers.

open Giraffe

let webApp =

choose [

route "/" >=> text "Hello, world"

route "/json" >=> json {| message = "hello" |}

route "/api/users" >=> requireAuth >=> getUsers

routef "/user/%i" (fun id -> sprintf "User %i" id |> text)

setStatusCode 404 >=> text "Not Found"

]

`choose` runs the first handler that matches. `>=>` says "let this handler pass, then chain the next." It looks like ordinary function composition, but it is a Kleisli composition over `Task<HttpContext option>`.

View — GiraffeViewEngine

You can build HTML in F#.

open Giraffe.ViewEngine

let layout (content: XmlNode list) =

html [] [

head [] [

title [] [ str "Giraffe" ]

link [ _rel "stylesheet"; _href "/style.css" ]

]

body [] content

]

let indexView =

layout [

h1 [] [ str "Hello, Giraffe" ]

p [] [ str "Functional ASP.NET Core" ]

]

Like JSX, but expressed as F# functions and lists, fully typed.

6. Bolero — Blazor + F#

Bolero started at IntelliFactory and is now maintained by the fsbolero organization. It **lets you build SPAs in F# on top of Blazor WebAssembly**.

Why Bolero

- Blazor is Microsoft's official framework for C# WebAssembly SPAs.

- Bolero layers F# and the Elmish (MVU) pattern on top.

- Result: **type safety + components + F#-DSL HTML + Elm-style state management**.

open Bolero

open Bolero.Html

open Elmish

type Model = { count: int }

type Message =

| Increment

| Decrement

let init () = { count = 0 }, Cmd.none

let update msg model =

match msg with

| Increment -> { model with count = model.count + 1 }, Cmd.none

| Decrement -> { model with count = model.count - 1 }, Cmd.none

let view model dispatch =

div {

h1 { "Bolero Counter" }

p { sprintf "Count: %d" model.count }

button {

on.click (fun _ -> dispatch Increment)

"+"

}

button {

on.click (fun _ -> dispatch Decrement)

"-"

}

}

Bolero's `div { }` and `button { }` are computation expressions. You can freely mix children, attributes, and event handlers inside.

Server-side vs WebAssembly

Bolero supports both hosting models.

- **Server-side Blazor** — UI updates flow through SignalR from the server.

- **Blazor WebAssembly** — F# code compiles to .NET IL and runs directly in the WASM runtime.

WebAssembly mode has no server load but ships a larger initial download. Bolero 3.x added AOT (Ahead-Of-Time) compilation that dramatically improves startup.

7. Fable — F# to JavaScript

Fable was started by Alfonso Garcia-Caro. It is now maintained by a core team led by Maxime Mangel.

Fable 5

Fable 5, released in 2024, ships multiple backends.

- **JavaScript** — the default. Works in Node.js and browsers.

- **TypeScript** — emits `.ts` files that drop into existing TS code.

- **Python** — Fable to Python: Jupyter notebooks, Python backends.

- **Rust** — experimental. Carries some F# safety into Rust.

- **Dart** — Flutter apps in F#.

// F# source

module App

open Browser

let main () =

let body = document.body

body.innerHTML <- "<h1>Hello from F#</h1>"

main ()

`dotnet fable` produces a JavaScript ESM module.

How it works

Fable uses `FSharp.Compiler.Service` to parse the F# source into an AST, then translates that AST into JavaScript (or another target). BCL functions map to Fable's own JS implementations of `Map`, `Seq`, `List`, `Option`, and so on.

With Fable.React / Feliz / Elmish

Fable itself is just a compiler. UI libraries are separate.

dotnet new -i Fable.Template

dotnet new fable-react

npm install

npm run start

The default Fable React template is the usual starting point.

8. Feliz — F# React-like DSL

Feliz is Zaid Ajaj's React DSL. It expresses React components more tersely and cleanly than Fable.React.

Example

open Feliz

let helloWorld = Html.div [

prop.className "container"

prop.children [

Html.h1 "Hello, Feliz"

Html.p "F# React without JSX"

]

]

[<ReactComponent>]

let Counter () =

let count, setCount = React.useState 0

Html.div [

Html.h2 (sprintf "Count: %d" count)

Html.button [

prop.onClick (fun _ -> setCount (count + 1))

prop.text "+"

]

]

- `Html.div [ ... ]` — a div tag.

- `prop.className`, `prop.onClick`, `prop.children` — every prop is strongly typed.

- `[<ReactComponent>]` — Feliz's compile plug-in turns the function into a React component.

- `React.useState`, `React.useEffect`, and every other hook are exposed with F# signatures.

You can use all of React without ever writing JSX.

Feliz.Bulma / Feliz.MaterialUI

The ecosystem is thick.

- **Feliz.Bulma** — a wrapper around the Bulma CSS framework.

- **Feliz.MaterialUI** — F# bindings for Material-UI components.

- **Feliz.Recharts** — Recharts chart library.

- **Feliz.Plotly** — Plotly.

Almost every popular React library has a Feliz wrapper.

9. Elmish — The Elm Pattern in F#

Elmish ports Elm's MVU (Model-View-Update) pattern to F#. It is the standard for state management in Bolero, Fable + React, and the SAFE Stack.

Three functions

// 1. initial state

let init () : Model * Cmd<Msg> = ...

// 2. message handling

let update (msg: Msg) (model: Model) : Model * Cmd<Msg> = ...

// 3. rendering

let view (model: Model) (dispatch: Msg -> unit) : ReactElement = ...

That is the whole thing. Far simpler than Redux or Vuex. If you know Elm, you can transcribe directly.

Example

open Elmish

open Elmish.React

open Feliz

type Model = { Count: int }

type Msg =

| Increment

| Decrement

| Reset

let init () = { Count = 0 }, Cmd.none

let update msg model =

match msg with

| Increment -> { model with Count = model.Count + 1 }, Cmd.none

| Decrement -> { model with Count = model.Count - 1 }, Cmd.none

| Reset -> { Count = 0 }, Cmd.none

let view model dispatch =

Html.div [

Html.h1 (sprintf "Count: %d" model.Count)

Html.button [

prop.onClick (fun _ -> dispatch Increment)

prop.text "+"

]

Html.button [

prop.onClick (fun _ -> dispatch Decrement)

prop.text "-"

]

Html.button [

prop.onClick (fun _ -> dispatch Reset)

prop.text "Reset"

]

]

Program.mkProgram init update view

|> Program.withReactSynchronous "elmish-app"

|> Program.run

The browser-mount code is the last four lines. `Program.mkProgram` takes init/update/view and builds the run loop.

Cmd — Effect isolation

`Cmd<Msg>` represents "side effects that should happen now." HTTP requests, timers, localStorage access — all expressed as commands.

let loadUserCmd userId =

Cmd.OfPromise.either

(fun () -> Fetch.fetchAs<User> (sprintf "/api/users/%d" userId))

()

UserLoaded

LoadFailed

The update function stays pure. Side effects escape via Cmd, which the runtime executes. Tests fall out trivially.

10. Avalonia.FuncUI — Declarative Desktop UI

Avalonia is the cross-platform .NET UI framework that picked up where WPF left off. The same code runs on Windows, macOS, Linux, iOS, Android, and WebAssembly.

Avalonia.FuncUI layers a **functional DSL based on F# computation expressions** on top of Avalonia.

Example

open Avalonia.FuncUI.DSL

open Avalonia.Controls

open Avalonia.Layout

let view () =

Component (fun ctx ->

let count = ctx.useState 0

DockPanel.create [

DockPanel.children [

TextBlock.create [

TextBlock.text (sprintf "Count: %d" count.Current)

TextBlock.horizontalAlignment HorizontalAlignment.Center

]

Button.create [

Button.content "+"

Button.onClick (fun _ -> count.Set (count.Current + 1))

]

Button.create [

Button.content "-"

Button.onClick (fun _ -> count.Set (count.Current - 1))

]

]

]

)

No XAML. Every UI fragment is F# code. A React-hook-style `useState` is built in.

Who uses it

- **JetBrains Rider** uses Avalonia for some auxiliary UIs (the main IntelliJ platform is Java/Kotlin, but separate tooling uses Avalonia).

- **Sirius A**, an F# community IRC/Matrix client.

- Internal tooling teams in .NET shops that want to ship F# desktop apps. Since Visual Studio for Mac shut down, this is roughly the only sane way to build .NET UI on macOS.

11. Tooling — Fantomas / Paket / FAKE / Polyglot Notebooks

Fantomas

Fantomas is the official F# formatter. Florian Verdonck and the core team maintain it.

dotnet tool install -g fantomas

fantomas src/

- Formats using the compiler's own AST, so the result is semantically identical to the input.

- Configured via `.editorconfig` or `fantomas-config.fsharpsettings`.

- Integrated with VS Code (Ionide), Rider, and Visual Studio.

Since 2024 Fantomas has lived in the same `fsprojects/fantomas` org alongside the F# compiler and is the de facto F# style standard.

Paket

Paket is an alternative package manager to NuGet, created by Steffen Forkmann.

Advantages:

- Pins transitive dependencies explicitly in a single `paket.lock` file. SDK-style PackageReference improved a lot, but its lock story is still subtle.

- Lets you add GitHub repos, gists, or HTTP URLs as dependencies.

- Especially popular in F# projects.

source https://api.nuget.org/v3/index.json

nuget Giraffe ~> 6.0

nuget FSharp.Data ~> 6.0

github fsharp/FSharp.Core

NuGet has improved enough to shrink Paket's footprint, but in large mono-repos it is still preferred.

FAKE

FAKE (F# Make) is the F#-based build script tool.

#r "paket: nuget Fake.Core.Target //"

#load ".fake/build.fsx/intellisense.fsx"

open Fake.Core

open Fake.IO

Target.create "Clean" (fun _ -> Shell.cleanDirs [ "bin"; "obj" ])

Target.create "Build" (fun _ ->

DotNet.build id "src/MyApp.fsproj"

)

Target.create "Test" (fun _ ->

DotNet.test id "tests/MyApp.Tests.fsproj"

)

"Clean" ==> "Build" ==> "Test"

Target.runOrDefault "Test"

The `==>` operator builds the dependency graph. Think Make, Rake, Gulp — but in F#.

In 2026 the combo of `dotnet` CLI plus GitHub Actions covers most pipelines without FAKE, so new projects use it less. Existing codebases still treat it as standard.

Polyglot Notebooks

Polyglot Notebooks is Microsoft's notebook environment. It feels like Jupyter but runs on .NET, and **you can mix F#, C#, PowerShell, JavaScript, SQL, and KQL cells in one notebook**.

Install the VS Code "Polyglot Notebooks" extension and you are done.

// F# cell

open Microsoft.DotNet.Interactive

#r "nuget: FSharp.Data, 6.4.0"

open FSharp.Data

let stocks = CsvProvider<"https://example.com/stocks.csv">.GetSample()

stocks.Rows |> Seq.take 5 |> Seq.iter (fun r -> printfn "%A" r)

The closest path F# has to replacing Python in data analysis, education, and reporting.

12. FSharp.Data — Type Providers

Type providers are F#'s killer feature. They **fetch the schema of an external data source at compile time and expose it as a strongly typed API with IDE autocomplete**.

CsvProvider

open FSharp.Data

type Stocks = CsvProvider<"https://example.com/stocks.csv">

let stocks = Stocks.GetSample()

for row in stocks.Rows do

printfn "%s: open=%f, close=%f" row.Date row.Open row.Close

`row.Date`, `row.Open`, and `row.Close` are strongly typed properties generated by the compiler reading the CSV header. If a column name changes, you get a compile error immediately.

JsonProvider

type GitHubRepo = JsonProvider<"https://api.github.com/repos/dotnet/fsharp">

let repo = GitHubRepo.GetSample()

printfn "Name: %s" repo.Name

printfn "Stars: %d" repo.StargazersCount

printfn "Default branch: %s" repo.DefaultBranch

The JSON response maps to types automatically. Renamed keys fail at compile time.

SqlProvider / WorldBankProvider / FreebaseProvider

- **SqlProvider** — connects to PostgreSQL, SQL Server, SQLite, or MySQL and exposes tables, views, and stored procedures as types.

- **WorldBankProvider** — strongly typed access to World Bank statistics.

- **FreebaseProvider** — Freebase is gone, but it was the canonical type-provider demo.

Type providers are a feature **unique to F#**. C#, Java, and Kotlin do not have this kind of metaprogramming surface. For data-heavy work, this alone is reason enough to choose F#.

13. MathNet.Numerics / DiffSharp — Numerics and Autodiff

MathNet.Numerics

MathNet.Numerics is the numerical-computing library for .NET. Think NumPy and SciPy for .NET.

open MathNet.Numerics.LinearAlgebra

let m = matrix [[ 1.0; 2.0 ]

[ 3.0; 4.0 ]]

let v = vector [ 5.0; 6.0 ]

let result = m * v

printfn "%A" result

// DenseVector 2-Double

// 17

// 39

let inv = m.Inverse()

printfn "%A" inv

- Dense and sparse matrices.

- BLAS / LAPACK acceleration (Intel MKL, OpenBLAS).

- Statistics, integration, optimization, FFT, distributions.

DiffSharp

DiffSharp is the autodiff library for .NET. Created by Atılım Güneş Baydin and collaborators. You **define tensor operations PyTorch-style and DiffSharp differentiates them automatically**.

open DiffSharp

let f (x: Tensor) = x ** 2.0 + 2.0 * x + 1.0

let x = dsharp.tensor [ 1.0; 2.0; 3.0 ]

let y = f x

let dy = dsharp.diff f x

printfn "y = %A" y // [4; 9; 16]

printfn "dy = %A" dy // [4; 6; 8] (i.e. 2x + 2)

DiffSharp backends include TorchSharp (libtorch C++ bindings) or a reference backend. GPU acceleration is supported.

Used for research code, differentiable programming, and gradient-based optimization.

14. TorchSharp + ML.NET — Machine Learning

TorchSharp

TorchSharp is the .NET PyTorch binding. It P/Invokes into libtorch C++. **You get an API that maps almost 1:1 with PyTorch but written in F# (or C#)**.

open TorchSharp

open type torch

let device = if cuda.is_available() then CUDA else CPU

let model =

Sequential(

("fc1", Linear(784L, 256L)),

("relu1", ReLU()),

("fc2", Linear(256L, 10L))

)

|> fun m -> m.``to``(device)

let x = randn([| 64L; 784L |]).``to``(device)

let y = model.forward(x)

printfn "Output shape: %A" y.shape

Type-safe tensor operations, GPU support, save and load models.

ML.NET

ML.NET is Microsoft's .NET ML framework. It is smooth from F# too.

open Microsoft.ML

open Microsoft.ML.Data

[<CLIMutable>]

type HouseData = {

Size: float32

Price: float32

}

[<CLIMutable>]

type Prediction = {

[<ColumnName("Score")>]

Price: float32

}

let mlContext = MLContext()

let trainingData = [

{ Size = 1.1f; Price = 1.2f }

{ Size = 1.9f; Price = 2.3f }

{ Size = 2.8f; Price = 3.0f }

{ Size = 3.4f; Price = 3.7f }

]

let dataView = mlContext.Data.LoadFromEnumerable trainingData

let pipeline =

mlContext.Transforms

.Concatenate("Features", "Size")

.Append(mlContext.Regression.Trainers.Sdca(labelColumnName = "Price", maximumNumberOfIterations = 100))

let model = pipeline.Fit dataView

let predictor = mlContext.Model.CreatePredictionEngine<HouseData, Prediction>(model)

let size = { Size = 2.5f; Price = 0f }

let prediction = predictor.Predict size

printfn "Predicted price for size %f: %f" size.Size prediction.Price

ML.NET covers gradient boosting, random forests, logistic regression, recommendation systems, text classification, image classification, and more. Importing TensorFlow and ONNX models is supported.

The `[<CLIMutable>]` attribute creates the setters ML.NET requires (F# records are immutable by default).

15. F# in Azure Functions / AWS Lambda

Azure Functions

The F# Isolated Worker model is standard on .NET 9.

namespace MyApp

open System

open Microsoft.Azure.Functions.Worker

open Microsoft.Azure.Functions.Worker.Http

open Microsoft.Extensions.Logging

type HelloFunction(logger: ILogger<HelloFunction>) =

[<Function("Hello")>]

member _.Run

([<HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")>] req: HttpRequestData)

=

logger.LogInformation "F# HTTP trigger function processed a request"

let response = req.CreateResponse(System.Net.HttpStatusCode.OK)

response.WriteString "Hello from F# on Azure Functions"

response

Microsoft itself treats F# as a first-class language on Azure Functions. Scaffold with `func init --worker-runtime dotnet-isolated --language fsharp`.

AWS Lambda

AWS's .NET 6/8 runtimes run F# Lambdas just fine.

namespace MyLambda

open Amazon.Lambda.Core

[<assembly: LambdaSerializer(typeof<Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer>)>]

do ()

type Handler() =

member _.Hello (input: string) (_: ILambdaContext) : string =

sprintf "Hello %s from F# Lambda" input

Deploy with the `Amazon.Lambda.Tools` CLI. Some larger shops write cloud automation scripts in F#.

16. Donald / Suave / Hopac / FParsec — The Rest

Donald — Thin SQL client

Donald is a thin helper over ADO.NET. Not an ORM like EF Core — you **write SQL directly and map the results to strongly typed records**.

open Donald

let conn = new System.Data.SqlClient.SqlConnection(connStr)

let sql = "SELECT id, name, email FROM users WHERE active = 1"

type User = { Id: int; Name: string; Email: string }

let readUser (rd: System.Data.IDataReader) =

{ Id = rd.ReadInt32 "id"

Name = rd.ReadString "name"

Email = rd.ReadString "email" }

let users =

conn

|> Db.newCommand sql

|> Db.query readUser

For when you do not want LINQ or an ORM and prefer SQL as-is.

Suave

Suave was created by Henrik Feldt. It is a self-hosted HTTP server and routing library that predates Saturn and Giraffe (around 2014). The distinguishing feature is **its own standalone HTTP server, not built on ASP.NET Core**, which made it popular for small side projects.

open Suave

open Suave.Filters

open Suave.Operators

open Suave.Successful

let app =

choose

[ GET >=> path "/" >=> OK "Hello, Suave"

GET >=> pathScan "/user/%d" (fun id -> OK (sprintf "User %d" id)) ]

startWebServer defaultConfig app

In 2026 new projects mostly default to Giraffe or Saturn, but Suave remains a lightweight option when attaching a tiny HTTP layer to a CLI or utility.

Hopac

Hopac is Vesa Karvonen's concurrency library. It ports the channels-and-jobs model of Concurrent ML / Reppy into F#.

open Hopac

open Hopac.Infixes

let producer (ch: Ch<int>) =

Job.forUpToIgnore 0 9 (fun i -> Ch.give ch i)

let consumer (ch: Ch<int>) =

Job.forUpToIgnore 0 9 (fun _ ->

Ch.take ch >>= fun v ->

Job.unit (printfn "Received: %d" v))

job {

let ch = Ch<int>()

do! Job.start (producer ch)

do! consumer ch

}

|> run

`Job` is a lightweight thread. Channel-based message passing, synchronous communication, and selection (`Alt`) come straight from Concurrent ML.

Use it when `async` or `Task` is not expressive enough or when you need high-performance message passing. Jet.com famously ran Hopac in parts of its pricing engine.

FParsec

FParsec is Stephan Tolksdorf's F# port of Parsec, the Haskell parser-combinator library.

open FParsec

let pInteger : Parser<int, unit> = pint32

let pPlus = pchar '+' >>. spaces

let pSum =

pInteger .>>. (pPlus >>. pInteger)

|>> (fun (a, b) -> a + b)

let result = run pSum "12 + 30"

match result with

| Success (v, _, _) -> printfn "Sum = %d" v

| Failure (err, _, _) -> printfn "Parse error: %s" err

Builds LL(1)+ backtracking parsers easily. The standard tool for DSLs, config-file parsers, and domain-specific languages.

17. Korea / Japan — Korean .NET Enterprise, Usac, MS Japan

Korea

Honestly, Korea's F# community is small. .NET itself in Korea lives mostly in game servers (especially Unity backends), parts of the financial sector, and public-sector SI work. F# shows up in a slice of those companies as a domain-modeling or data-pipeline tool.

- **Some Kakao and Line data-analysis scripts** — F# pops up as an ad-hoc analysis tool through Polyglot Notebooks (no official announcement; it surfaces in conference side-talks).

- **NHN, Netmarble, Pearl Abyss** and similar game companies use F# in back-office tools.

- **Finance** — modeling tools at brokerages and asset managers, but few public case studies.

- **MSDN.kr and the .NET Korea User Group** — Facebook group and Discord. There is no dedicated F# Korean group; F# topics surface inside the broader .NET community.

Very few F# books have Korean translations. Most readers go straight to the English *Expert F#* or *Stylish F#*.

Japan

Japan's F# community is denser than Korea's.

- **ユーザックシステム (Usac System)** — an RPA and logistics-automation company with offices in Tokyo, Osaka, and Nagoya. They ship parts of their product in F#. Microsoft Japan's F# evangelist Kazuya Kawakami (@kos59125) cites them frequently.

- **F# Tokyo** — an annual user meetup that has crossed 100 attendees in some years.

- **F# Online Meetup Japan** — has run regularly online since 2022, post-COVID.

- **Microsoft Japan F# evangelists** — Microsoft Japan has consistently pushed F# in their .NET community. Kazuhisa Yokota and Kazuya Kawakami have held that seat.

- **Toppan Printing (凸版印刷)** and parts of **NTT Data** — internal tools and analytics pipelines have been reported to use F#.

Japanese F# material is far thicker than Korean. The Qiita `fsharp` tag has hundreds of posts. Don Syme's books exist in Japanese translation too.

18. Who Should Learn F#

Honestly:

**Worth learning if you are**

- **A C# developer working on .NET backends** — same BCL, same runtime, same NuGet packages, plus functional style and domain modeling. C# 9's records, pattern matching, and switch expressions were inspired largely by F#. Learning F# tells you where C# is heading.

- **A domain modeler in finance, insurance, logistics, or healthcare** — F#'s ability to express domains via discriminated unions is in a different league. Scott Wlaschin's slogan "make illegal states unrepresentable" actually holds here.

- **Someone who knows OCaml or Haskell but cannot find an industry job** — F# is an ML-family cousin sitting on the massive industrial base of .NET. It can be the answer to "where do I get paid for my functional skills?"

- **Anyone who wants to do strongly typed data analysis** — type providers really are different in kind.

**Skip it if you are**

- **Never going to touch .NET** — F# runs on .NET. If you refuse the platform, Haskell, OCaml, or Scala are more sensible.

- **A purist functional programmer** — F# is multi-paradigm. Mutability is allowed. OOP classes are allowed. If you want Haskell-grade purity, you will be disappointed.

- **Optimizing for the shortest learning curve** — F# itself is not hard, but "learn F# without learning C# and the BCL and ASP.NET Core" is essentially impossible. You sign up for the whole .NET ecosystem.

F#'s real value is **"ML-style functional domain modeling on the massive industrial infrastructure of .NET."** If that combination is attractive, F# is worth your time.

19. References

- F# 9 announcement — `devblogs.microsoft.com/dotnet/announcing-fsharp-9`

- F# language reference — `learn.microsoft.com/dotnet/fsharp/language-reference`

- F# Foundation — `fsharp.org`

- .NET 9 announcement — `devblogs.microsoft.com/dotnet/announcing-dotnet-9`

- F# on GitHub — `github.com/dotnet/fsharp`

- Saturn — `saturnframework.org`

- Giraffe — `giraffe.wiki`

- Bolero — `fsbolero.io`

- Fable — `fable.io`

- Feliz — `zaid-ajaj.github.io/Feliz`

- Elmish — `elmish.github.io`

- Avalonia.FuncUI — `funcui.avaloniaui.net`

- Avalonia — `avaloniaui.net`

- Fantomas — `fsprojects.github.io/fantomas`

- Paket — `fsprojects.github.io/Paket`

- FAKE — `fake.build`

- Polyglot Notebooks — `code.visualstudio.com/docs/polyglot`

- FSharp.Data — `fsprojects.github.io/FSharp.Data`

- MathNet.Numerics — `numerics.mathdotnet.com`

- DiffSharp — `diffsharp.github.io`

- TorchSharp — `github.com/dotnet/TorchSharp`

- ML.NET — `dotnet.microsoft.com/apps/machinelearning-ai/ml-dotnet`

- Donald — `github.com/pimbrouwers/Donald`

- Suave — `suave.io`

- Hopac — `github.com/Hopac/Hopac`

- FParsec — `www.quanttec.com/fparsec`

- F# in Azure Functions — `learn.microsoft.com/azure/azure-functions/functions-reference-fsharp`

- SAFE Stack — `safe-stack.github.io`

- F# Software Foundation Slack — `fsharp.org/guides/slack`

- F# Tokyo — `fsugjp.connpass.com`

- Don Syme's blog — `dsyme.github.io`

- Scott Wlaschin — Domain Modeling Made Functional — `fsharpforfunandprofit.com`

- Krzysztof Cieslak — Ionide / Saturn — `kcieslak.io`

현재 단락 (1/582)

Every year someone asks, "F#? People still use that?"

작성 글자: 0원문 글자: 27,213작성 단락: 0/582