Skip to content
Published on

모던 F# 2026 — F# 9 (.NET 9) / Saturn / Giraffe / Bolero / Fable / Feliz / Elmish / Fantomas 심층 가이드

Authors

프롤로그 — 2026년에도 F#을 쓰는 사람들이 있다

매년 누군가는 묻는다. "F#, 아직도 써요?"

쓴다. 그것도 .NET 진영 안에서 점점 더 진지하게 쓴다.

  • Jet.com(현재 Walmart Labs 일부)은 2010년대 중반부터 가격 결정 엔진을 F#으로 짰고, 인수 뒤에도 코어 가격 모델 일부는 F#으로 남았다.
  • Microsoft 본사 안에서는 컴파일러 팀과 Azure 일부 서비스(특히 비용 계산, 라이센스, 분석 도구)가 F#을 쓴다. F# 자체가 Microsoft Research Cambridge에서 Don Syme가 2005년부터 만든 언어다.
  • CitiBank, Credit Suisse, Standard Chartered, Bank of America — 금융 도메인의 가격 결정·리스크 모델에서 F#은 꾸준한 한 자리를 차지한다. 도메인 모델링이 강하고, 부수효과를 타입으로 가두기 좋아서다.
  • AT&T, Demetrix, Indaba Music, GameSys, Tachyus, G-Research — 데이터 파이프라인, 머신러닝, 게임 백엔드에서 F# 코드가 돈다.
  • 일본의 **ユーザックシステム(Usac System)**은 도쿄·오사카에서 자동화·물류 솔루션을 F#과 .NET으로 만든다. MS Japan의 F# 에반젤리스트들이 이 회사를 자주 언급한다.

2010년대 F#의 적은 "쓰기 어렵다"가 아니었다. 진짜 적은 **"Mono 시절에는 비-Windows에서 빌드가 깨지고, Visual Studio for Mac은 절반만 동작하며, Paket인지 NuGet인지 결정하기 어렵다"**였다. 그게 2026년에는 어떻게 되어 있을까.

답: .NET 9가 진짜로 크로스플랫폼이 되었고, F# 9는 nullable reference types를 완성했으며, Fantomas가 표준 포매터로 자리 잡았다. 그래서 더 이상 "내 노트북에서는 안 빌드된다" 같은 일이 거의 없다. 그리고 dotnet CLI 하나로 Windows, macOS, Linux에서 동일하게 굴러간다.

이 글은 2026년의 F# 스택을 한 호흡으로 본다. 컴파일러부터 웹 프레임워크, 클라이언트 사이드, 데스크톱 UI, 빌드 도구, 데이터, 수치·ML, SQL, 파서, 그리고 사람들까지.


1장 · 2026년 모던 F# — .NET 9의 시대

먼저 한 장의 풍경.

                  모던 F# 2026 스택

[런타임]           .NET 9 (2024.11 LTS) ── 크로스플랫폼 단일 SDK
[컴파일러]         F# 9 (2024.11) / F# 10 preview (GitHub main)
[빌드]             dotnet CLI / FAKE / Paket / MSBuild

[웹 백엔드]        Saturn (Rails 스타일, opinionated)
                   Giraffe (ASP.NET Core 함수형 미들웨어)
                   Suave (구식, 자체 HTTP)

[웹 클라이언트]    Fable (F# to JS) + Feliz (React) + Elmish (Elm pattern)
                   Bolero (Blazor WebAssembly + F#)

[데스크톱]         Avalonia.FuncUI (선언적 크로스플랫폼 UI)

[도구]             Fantomas (포매터) / Ionide (VS Code) / FsAutoComplete
                   Polyglot Notebooks (Jupyter on .NET)

[데이터]           FSharp.Data (CSV/JSON/XML/SQL type providers)
                   Donald (얇은 SQL 클라이언트)

[수치·ML]          MathNet.Numerics (선형대수)
                   DiffSharp (자동미분, PyTorch 스타일)
                   TorchSharp (libtorch 바인딩) + ML.NET

[병행성]           Hopac (concurrent ML 스타일)
                   async / Task

[파서]             FParsec (Parsec 포팅)

[클라우드]         F# in Azure Functions / AWS Lambda
                   F# in Aspire (분산 .NET 워크플로)

[프로덕션 사용자]  Jet.com (Walmart) / Microsoft / CitiBank /
                   Credit Suisse / GameSys / Tachyus / Usac

이게 2026년의 F# 지형도다. 박스 하나하나 들어가 보자.


2장 · F# 9 (2024.11, .NET 9 동시 출시) — nullable + DU 개선

F# 9는 2024년 11월, .NET 9와 함께 정식 출시되었다. .NET 9는 12개월 STS(Standard Term Support)이지만, F# 9 자체는 SDK에 묶여서 다음 .NET 10 LTS(2025년 11월 예정)까지 자연스럽게 따라간다.

핵심 변화는 셋이다.

Nullable reference types 완성

C#은 2019년 C# 8에서 ?를 통한 nullable reference types를 도입했다. F#은 자체적으로 Option<'T>('T option)를 가지고 있어서 한참 동안 "F# 코드에서는 null이 거의 없다"는 입장을 유지했다. 하지만 BCL(C#으로 짠 .NET 표준 라이브러리)과 상호운용할 때마다 null이 새어 들어왔다.

F# 9는 interop 경계에서 nullable reference types를 1급 시민으로 만든다.

// F# 9: nullable annotation
let processName (name: string | null) =
    match name with
    | null -> "(anonymous)"
    | n -> n.ToUpper()

// C# 메서드를 호출할 때도 안전
let s: string | null = System.Environment.GetEnvironmentVariable("HOME")
match s with
| null -> printfn "HOME not set"
| home -> printfn "HOME = %s" home

string | null은 C# 9의 string?과 정확히 대응한다. 컴파일러는 이걸 검사해서 "null을 unwrap 하지 않은 채로 메서드 호출하면 경고"를 띄운다.

Discriminated union 개선

DU(discriminated union)는 F#의 영혼이다. F# 9는 두 가지를 개선했다.

  1. Struct DU의 박싱 비용 절감[<Struct>] DU를 더 적극적으로 stack에 머무르게 한다.
  2. Union case의 가시성 제어private/internal 가시성을 union의 각 case에도 적용할 수 있다.
[<Struct>]
type Result<'T, 'E> =
    | Ok of value: 'T
    | Error of error: 'E

// 가시성이 다른 case
type DbHandle =
    private | Open of System.Data.IDbConnection
            | Closed

표준 라이브러리 개선

  • List.randomShuffle, Array.randomChoice 등 무작위성 헬퍼 추가.
  • Result 모듈 확장: Result.toList, Result.toOption, Result.toArray.
  • Seq.tryExactlyOne — 정확히 한 개의 요소만 있을 때 Some, 아니면 None.
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 — GitHub에서 굴러가는 중

F# 10은 GitHub dotnet/fsharp 메인 브랜치에서 활발히 개발 중이다. 2026년 5월 현재, 다음 후보 기능이 RFC(Request for Comments)에 올라와 있다.

tasks let! 분기 단축

task { ... } 안에서 여러 비동기 결과를 합칠 때 let! 패턴이 길어지던 문제. F# 10은 이걸 짧게 쓸 수 있는 표현식 빌더 확장을 검토 중이다.

// F# 9 스타일
task {
    let! a = getUserAsync userId
    let! b = getOrdersAsync userId
    return (a, b)
}

// F# 10 preview에서 검토 중인 short form
task {
    let! (a, b) = getUserAsync userId, getOrdersAsync userId
    return (a, b)
}

Type provider 비동기화

FSharp.DataJsonProvider, CsvProvider는 컴파일 타임에 스키마를 가져와서 타입을 만든다. 큰 스키마에서 IDE가 멈추는 문제가 있었는데, F# 10은 type provider 호출을 비동기로 만드는 인프라를 도입하려 한다.

Resumable code 개선

F# 6에서 도입된 resumable code(task { }, taskSeq { }의 기반)는 컴파일러 내부 기능이지만, F# 10은 이걸 사용자에게도 더 안전하게 노출하려 한다. Hopac이나 자체 effect 라이브러리를 만들 때 도움이 된다.

Self-type constraints

C# 11의 static abstract 인터페이스 멤버에 대응하는 F# 측 문법. SRTP(Statically Resolved Type Parameters)를 더 자연스럽게 쓸 수 있게 한다.

F# 10은 .NET 10 LTS와 함께 2025년 11월 출시 예정이다.


4장 · Saturn — opinionated 웹 프레임워크 (Giraffe 기반)

Saturn은 Krzysztof Cieslak(SAFE Stack 메인테이너, Ionide 만든 사람)이 만든 풀스택 웹 프레임워크다. Giraffe 위에 깔린 Rails 같은 레이어라고 보면 된다.

핵심 컨셉

Saturn은 application builder DSL을 제공한다. ASP.NET Core의 미들웨어 파이프라인을 F# 빌더 구문으로 깔끔하게 정리한 것.

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

이게 전부다. application { } 안의 각 토큰은 사실 함수다. Saturn은 application computation expression을 정의하고, 각 키워드(use_router, url, ...)는 ApplicationBuilder 상에서 작동한다.

Controller — MVC 스타일

Saturn은 controller도 builder로 제공한다.

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
}

REST 7개 액션이 한 builder에 다 들어간다. Rails 사용자에게는 친숙한 구조다.

SAFE Stack

Saturn은 단독으로 쓰일 수도 있지만, 보통 SAFE Stack의 일부로 쓰인다.

  • S — Saturn (서버)
  • A — Azure (호스팅)
  • F — Fable (클라이언트, F# to JS)
  • E — Elmish (Elm 패턴)

이 네 글자가 SAFE다. dotnet new SAFE로 풀스택 F# 프로젝트가 만들어진다. 클라이언트와 서버가 같은 F# 도메인 타입을 공유하는 것이 가장 큰 매력이다.


5장 · Giraffe — 함수형 ASP.NET Core

Giraffe는 Saturn의 아래쪽 절반이다. ASP.NET Core의 HttpContext 파이프라인을 함수형으로 감싼 라이브러리.

HttpHandler

Giraffe의 핵심 추상은 HttpHandler다.

type HttpHandler = HttpFunc -> HttpContext -> HttpFuncResult
// 여기서 HttpFunc = HttpContext -> HttpFuncResult
//      HttpFuncResult = Task<HttpContext option>

즉, "다음 핸들러"와 "현재 컨텍스트"를 받아 "수정된 컨텍스트(또는 None)"를 비동기로 돌려주는 함수. 이게 ASP.NET Core 미들웨어 한 칸과 정확히 대응한다.

합성

>=> 연산자로 핸들러를 합성한다.

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는 첫 번째로 매칭되는 핸들러를 실행한다. >=>는 "이 핸들러를 통과시키고 다음을 이어 붙여라"라는 뜻. 함수 합성과 정확히 같은 모양이지만, Task<HttpContext option>을 다루는 Kleisli composition이다.

View — GiraffeViewEngine

HTML을 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" ]
    ]

JSX와 비슷하지만, F# 함수와 리스트로 표현된다. 정적 타입이 보장된다.


6장 · Bolero — Blazor + F#

Bolero는 IntelliFactory에서 시작해 지금은 fsbolero 조직에서 유지하는 프로젝트로, Blazor WebAssembly 위에서 F#으로 SPA를 만들게 해 준다.

왜 Bolero인가

  • Blazor는 C#으로 WebAssembly SPA를 만드는 Microsoft의 공식 프레임워크다.
  • Bolero는 그 위에 F#과 Elmish(MVU) 패턴을 얹는다.
  • 결과: 타입 안전 + 컴포넌트 + F# DSL로 만든 HTML + Elm 스타일 상태 관리.
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의 div { }, button { }은 computation expression이다. 안에 자식 요소·속성·이벤트 핸들러를 자유롭게 섞을 수 있다.

Server-side vs WebAssembly

Bolero는 두 가지 호스팅 모델을 다 지원한다.

  • Server-side Blazor — UI 업데이트가 SignalR을 통해 서버에서 일어남.
  • Blazor WebAssembly — F# 코드가 .NET IL로 컴파일되고, WASM 런타임에서 직접 실행.

WebAssembly 모드는 서버 부하가 없지만 초기 다운로드가 큰 편. Bolero 3.x부터 AOT(Ahead Of Time) 컴파일을 지원해 시작 속도가 많이 좋아졌다.


7장 · Fable — F# to JavaScript

Fable은 Alfonso Garcia-Caro가 시작한 F# to JavaScript 컴파일러다. 지금은 Maxime Mangel을 비롯한 코어 팀이 유지한다.

Fable 5

2024년 출시된 Fable 5는 여러 백엔드를 지원한다.

  • JavaScript — 기본 백엔드. Node.js, 브라우저에서 동작.
  • TypeScript.ts 파일로 출력. 다른 TS 코드와 자연스럽게 섞인다.
  • Python — F#을 Python으로 변환. Jupyter 노트북, 파이썬 백엔드.
  • Rust — 실험적. F#의 일부 안전성을 Rust로 옮겨 본다.
  • Dart — Flutter 앱을 F#으로 짤 수 있다.
// F# 소스
module App

open Browser

let main () =
    let body = document.body
    body.innerHTML <- "<h1>Hello from F#</h1>"

main ()

이걸 dotnet fable로 컴파일하면 JavaScript ESM이 나온다.

동작 방식

Fable은 F# 컴파일러 서비스(FSharp.Compiler.Service)를 사용해 F# 소스를 AST로 파싱한 뒤, 그 AST를 JavaScript(또는 다른 타깃)로 변환한다. .NET BCL 함수는 Fable의 자체 JS 구현으로 매핑된다(Map, Seq, List, Option 등).

Fable.React / Feliz / Elmish와 함께

Fable 자체는 컴파일러일 뿐. UI 라이브러리는 별도로 깔아서 쓴다.

dotnet new -i Fable.Template
dotnet new fable-react
npm install
npm run start

기본 Fable React 템플릿이 시작점이다.


8장 · Feliz — F# React-like DSL

Feliz는 Zaid Ajaj가 만든 React DSL이다. Fable.React보다 짧고 깔끔하게 React 컴포넌트를 표현한다.

예제

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 [ ... ] — div 태그.
  • prop.className, prop.onClick, prop.children — 모든 prop이 강타입.
  • [<ReactComponent>] — Feliz 컴파일 플러그인이 React 컴포넌트로 변환.
  • React.useState, React.useEffect 등 모든 hook이 F# 시그니처로 노출.

JSX 없이 F# 문법만으로 React를 다 쓸 수 있다.

Feliz.Bulma / Feliz.MaterialUI

생태계가 두텁다.

  • Feliz.Bulma — Bulma CSS 프레임워크 래퍼.
  • Feliz.MaterialUI — Material-UI 컴포넌트의 F# 바인딩.
  • Feliz.Recharts — Recharts 차트 라이브러리.
  • Feliz.Plotly — Plotly.

거의 모든 인기 React 라이브러리에 Feliz 래퍼가 있다.


9장 · Elmish — Elm 패턴 in F#

Elmish는 Elm 언어의 MVU(Model-View-Update) 패턴을 F#으로 옮긴 라이브러리다. Bolero, Fable + React, SAFE Stack의 상태 관리 표준이다.

세 가지 함수

// 1. 초기 상태
let init () : Model * Cmd<Msg> = ...

// 2. 메시지 처리
let update (msg: Msg) (model: Model) : Model * Cmd<Msg> = ...

// 3. 렌더링
let view (model: Model) (dispatch: Msg -> unit) : ReactElement = ...

이게 전부다. Redux나 Vuex보다 훨씬 단순하다. Elm을 안다면 그대로 옮겨 적으면 된다.

예시

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

브라우저에 띄우는 코드는 마지막 4줄이 전부다. Program.mkProgram이 init/update/view를 받아 실행 루프를 만든다.

Cmd — 부수효과 격리

Cmd<Msg>는 "지금 일어나야 할 부수효과"를 표현하는 타입이다. HTTP 요청, 타이머, localStorage 접근 등이 다 Cmd로 표현된다.

let loadUserCmd userId =
    Cmd.OfPromise.either
        (fun () -> Fetch.fetchAs<User> (sprintf "/api/users/%d" userId))
        ()
        UserLoaded
        LoadFailed

update 함수는 순수하다. 부수효과는 Cmd로 빠져나가서 runtime이 실행한다. 테스트하기가 매우 쉽다.


10장 · Avalonia.FuncUI — 선언적 데스크톱 UI

Avalonia는 WPF의 정신을 이어받은 크로스플랫폼 .NET UI 프레임워크다. Windows, macOS, Linux, iOS, Android, WebAssembly에서 동일한 코드가 돈다.

Avalonia.FuncUI는 그 Avalonia 위에 F# computation expression 기반의 함수형 DSL을 얹은 것.

예시

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))
                ]
            ]
        ]
    )

XAML이 없다. 모든 UI가 F# 코드로 표현된다. React의 hook과 비슷한 useState 패턴이 있다.

누가 쓰나

  • JetBrains Rider의 일부 UI는 Avalonia로 만들어진다(IntelliJ 플랫폼 자체는 Java/Kotlin이지만, 별도 도구는 Avalonia).
  • Sirius A(F# 커뮤니티의 IRC/Matrix 클라이언트).
  • 사내 도구를 F#으로 짜고 싶은 .NET 팀들. Visual Studio for Mac이 종료된 뒤로 macOS에서 .NET UI를 짤 수 있는 거의 유일한 옵션이다.

11장 · 도구 — Fantomas / Paket / FAKE / Polyglot Notebooks

Fantomas

Fantomas는 F#의 공식 포매터다. Florian Verdonck를 비롯한 코어 팀이 유지한다.

dotnet tool install -g fantomas
fantomas src/
  • 컴파일러의 AST를 직접 사용해 포매팅한다. 따라서 결과가 의미적으로 동일함이 보장된다.
  • .editorconfig 또는 fantomas-config.fsharpsettings로 스타일을 조정한다.
  • VS Code(Ionide), Rider, Visual Studio 통합 지원.

2024년부터 Fantomas는 F# 컴파일러와 같은 저장소(fsprojects/fantomas)에서 관리되며, F# 코드 컨벤션의 사실상 표준이 되었다.

Paket

Paket은 NuGet의 대안 패키지 매니저다. Forki Steffen Forkmann이 만들었다.

장점:

  • 트랜시티브 의존성을 paket.lock 한 파일에 명시적으로 고정한다(NuGet PackageReference는 SDK 스타일로 많이 좋아졌지만 lock은 여전히 미묘하다).
  • GitHub repo, gist, HTTP URL을 직접 의존성으로 추가할 수 있다.
  • F# 프로젝트에서 특히 인기.
source https://api.nuget.org/v3/index.json

nuget Giraffe ~> 6.0
nuget FSharp.Data ~> 6.0
github fsharp/FSharp.Core

NuGet이 충분히 좋아져서 Paket의 입지는 줄어들었지만, 큰 모노레포에서는 여전히 선호된다.

FAKE

FAKE(F# Make)는 F#으로 빌드 스크립트를 짜는 도구다.

#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"

==> 연산자가 의존성 그래프를 만든다. Make, Rake, Gulp의 F# 버전이라고 보면 된다.

2026년에는 dotnet CLI와 GitHub Actions의 조합으로 FAKE 없이도 빌드 파이프라인을 짤 수 있어서 신규 프로젝트에서는 덜 쓰이지만, 기존 코드베이스에서는 여전히 표준이다.

Polyglot Notebooks

Polyglot Notebooks는 Microsoft의 노트북 환경이다. Jupyter와 같은 UX지만 .NET 위에서 돌고, 한 노트북 안에 F#, C#, PowerShell, JavaScript, SQL, KQL 셀을 섞을 수 있다.

VS Code 확장 Polyglot Notebooks를 설치하면 끝.

// F# 셀
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)

데이터 분석, 교육, 리포트 작성에서 F#이 Python을 대체할 수 있는 거의 유일한 통로다.


12장 · FSharp.Data — Type Providers

Type provider는 F#의 킬러 피처다. 컴파일 타임에 외부 데이터 소스의 스키마를 가져와서, IDE의 자동완성을 통해 강타입으로 접근하게 해 준다.

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, row.Close는 컴파일러가 CSV 헤더를 읽어서 만든 강타입 프로퍼티다. 컬럼명이 바뀌면 컴파일 에러로 즉시 발견된다.

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

JSON 응답을 타입으로 자동 매핑. 키 이름이 바뀌면 컴파일러가 잡아 준다.

SqlProvider / WorldBankProvider / FreebaseProvider

  • SqlProvider — PostgreSQL, SQL Server, SQLite, MySQL에 접속해서 테이블/뷰/저장 프로시저를 타입으로 노출.
  • WorldBankProvider — World Bank 통계 데이터에 강타입 접근.
  • FreebaseProvider — (Freebase가 종료되어 지금은 잘 안 쓰이지만 type provider의 시연 예제로 유명했다.)

Type provider는 F#만이 가진 기능이다. C#, Java, Kotlin에는 이런 메타프로그래밍 면이 없다. 데이터 처리 프로젝트라면 이거 하나만으로도 F#을 쓸 가치가 있다.


13장 · MathNet.Numerics / DiffSharp — 수치 / 자동미분

MathNet.Numerics

MathNet.Numerics는 .NET용 수치 계산 라이브러리다. NumPy, SciPy의 .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 / Sparse 행렬.
  • BLAS / LAPACK 가속 옵션(Intel MKL, OpenBLAS).
  • 통계, 적분, 최적화, FFT, 분포 등.

DiffSharp

DiffSharp는 .NET용 자동미분 라이브러리다. Atılım Güneş Baydin 등이 만들었다. PyTorch처럼 텐서 연산을 정의하면 그 미분을 자동으로 계산해 준다.

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]   (즉 2x + 2)

DiffSharp는 백엔드로 TorchSharp(libtorch C++ 바인딩)나 자체 reference backend를 쓸 수 있다. GPU 가속도 지원.

연구 코드, 미분 가능 프로그래밍, 그래디언트 기반 최적화에 쓰인다.


14장 · TorchSharp + ML.NET — 머신러닝

TorchSharp

TorchSharp는 .NET용 PyTorch 바인딩이다. libtorch C++ 라이브러리를 P/Invoke로 부른다. PyTorch와 거의 1:1 대응되는 API를 F#에서 쓸 수 있다.

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

타입 안전한 텐서 연산, GPU 지원, 모델 저장/로드.

ML.NET

ML.NET은 Microsoft의 .NET 머신러닝 프레임워크다. F#에서도 매끄럽게 쓸 수 있다.

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은 그래디언트 부스팅, 랜덤 포레스트, 로지스틱 회귀, 추천 시스템, 텍스트 분류, 이미지 분류 등 폭넓은 알고리즘을 지원한다. TensorFlow / ONNX 모델 임포트도 가능.

F#의 [<CLIMutable>] 어트리뷰트는 ML.NET이 요구하는 setter를 만들어 준다(F# 레코드는 기본적으로 immutable이라서).


15장 · F# in Azure Functions / AWS Lambda

Azure Functions

F# Isolated Worker model은 .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 자체가 F#을 Azure Functions의 1급 언어로 지원한다. func init --worker-runtime dotnet-isolated --language fsharp로 템플릿 생성.

AWS Lambda

AWS의 .NET 6/8 런타임에서 F# Lambda를 실행할 수 있다.

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

Amazon.Lambda.Tools CLI로 배포한다. 큰 회사에서는 클라우드 자동화 스크립트를 F#으로 짜는 사례가 있다.


16장 · Donald / Suave / Hopac / FParsec — 그 외

Donald — 얇은 SQL 클라이언트

Donald는 ADO.NET 위에 깔린 가벼운 SQL 헬퍼다. EF Core 같은 ORM이 아니라, SQL을 그대로 쓰고 결과를 강타입으로 매핑하는 방식.

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

LINQ도 ORM도 싫고 SQL 그대로 쓰고 싶을 때.

Suave

Suave는 Henrik Feldt가 만든 자체 HTTP 서버 + 라우팅 라이브러리. ASP.NET Core 이전 시대(2014~)부터 존재했다. ASP.NET Core 위에 안 깔려 있는 독립 웹 서버라는 게 특징이고, 작은 사이드 프로젝트에서 인기가 있었다.

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

2026년에는 신규 프로젝트에서 Giraffe/Saturn에 자리를 거의 다 내줬지만, 작은 도구·CLI에 HTTP 인터페이스를 붙일 때 가벼운 선택지로 남아 있다.

Hopac

Hopac은 Vesa Karvonen이 만든 동시성 라이브러리다. Concurrent ML / Reppy의 channels-and-jobs 모델을 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<'T>는 lightweight thread다. 채널 기반 메시지 전달, 동기 통신, 선택(Alt) 같은 Concurrent ML 패턴을 그대로 쓸 수 있다.

async/Task보다 더 표현력 있는 동시성이 필요할 때, 고성능 메시지 패싱이 필요할 때 쓴다. Jet.com이 가격 결정 엔진의 일부에 Hopac을 썼다는 사례가 유명하다.

FParsec

FParsec은 Stephan Tolksdorf의 Parsec(Haskell 파서 콤비네이터) F# 포팅이다.

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

LL(1) 이상의 백트래킹 파서를 쉽게 만든다. DSL, 설정 파일 파서, 도메인 특화 언어를 만들 때 표준 도구.


17장 · 한국 / 일본 — 한국 .NET 엔터프라이즈, Usac, MS Japan

한국

한국의 F# 커뮤니티는 솔직히 작다. .NET 자체가 한국에서는 게임 서버(특히 유니티 백엔드), 일부 금융권, 공공기관 SI 정도에 집중된다. F#은 그 중 일부 회사에서 도메인 모델링이나 데이터 파이프라인 도구로 쓰인다.

  • 카카오 / 라인의 일부 데이터 분석 스크립트 — Polyglot Notebooks를 통해 F#이 ad-hoc 분석에 쓰이는 경우가 있다(공식 발표는 없음, 컨퍼런스 사이드 토크에서 자주 나오는 이야기).
  • NHN, 넷마블, 펄어비스 등 게임 회사의 일부 백오피스 도구.
  • 금융권 — 일부 증권사·자산운용사에서 모델링 도구로 F#을 쓰는 사례가 있지만 공개된 케이스 스터디는 적다.
  • MSDN.kr / .NET Korea User Group — 페이스북 그룹과 디스코드. F# 전용 한국 모임은 따로 없고 .NET 전체 모임에서 가끔 F# 이야기가 나온다.

한국에서 F# 책으로 한국어로 번역된 것은 거의 없다. Don Syme의 Expert F# 영문판이나 Stylish F# 영문판을 보는 것이 보통이다.

일본

일본은 한국보다 F# 커뮤니티가 두텁다.

  • ユーザックシステム(Usac System) — 도쿄·오사카·나고야의 RPA / 물류 자동화 회사. 자체 제품 일부를 F#으로 짠다. Microsoft Japan의 F# 에반젤리스트 가와카미 산(@kos59125, Kazuya Kawakami)이 자주 인용한다.
  • F# Tokyo — 매년 열리는 사용자 모임. 100명 단위로 모이는 해도 있다.
  • F# Online Meetup Japan — 코로나 이후 온라인 위주로 전환되어 2022년부터 정기적으로 열린다.
  • Microsoft Japan의 F# evangelists — Microsoft가 일본 .NET 커뮤니티에서 F#을 꾸준히 푸시했다. Kazuhisa Yokota, Kazuya Kawakami 등이 그 자리에 있었다.
  • 凸版印刷 (Toppan Printing), NTT Data 일부 — 사내 도구·분석 파이프라인에서 F# 사용 사례가 보고된 적이 있다.

일본어 F# 자료가 한국어보다 많다. Qiita의 fsharp 태그에는 수백 개 글이 있다. Don Syme의 책 일본어판도 존재한다.


18장 · 누가 F#을 배워야 하나

솔직하게.

배우면 좋은 사람

  • .NET 백엔드를 짜는 C# 개발자 — 같은 BCL, 같은 런타임, 같은 NuGet 패키지를 쓰면서 함수형 스타일과 도메인 모델링을 얻는다. C# 9에서 들어온 records, pattern matching, switch expressions는 사실상 F#에서 영감을 받았다. F#을 알면 C#이 어디로 가고 있는지 미리 안다.
  • 금융·보험·로지스틱스·헬스케어 도메인의 모델러 — discriminated union으로 도메인을 표현하는 능력이 다른 언어와 차원이 다르다. "불가능한 상태를 표현 불가능하게 만든다(make illegal states unrepresentable)"는 Scott Wlaschin의 슬로건이 진짜로 성립한다.
  • OCaml / Haskell을 알지만 산업에서 쓸 회사를 찾지 못한 사람 — F#은 ML 계열의 친척이고, .NET이라는 거대한 산업 기반 위에서 돈다. "내 함수형 지식을 어디서 써먹지?"의 답이 될 수 있다.
  • 데이터 분석을 강타입으로 하고 싶은 사람 — type provider는 정말 다른 차원의 도구다.

안 배워도 되는 사람

  • .NET을 절대 안 쓸 사람 — F#은 .NET 위에서 돈다. .NET을 안 쓸 거면 Haskell, OCaml, Scala가 더 합리적이다.
  • 순수성에 집착하는 함수형 마니아 — F#은 멀티패러다임이다. 가변성도 허용하고, OOP 클래스도 쓸 수 있다. Haskell 같은 순수성을 원한다면 실망할 수 있다.
  • 빠른 학습 곡선이 우선인 사람 — F# 자체는 어렵지 않지만, "C#과 BCL과 ASP.NET Core를 모르면서 F#만 배운다"는 것은 사실상 불가능하다. .NET 생태계 학습이 같이 따라온다.

F#의 진짜 가치는 **".NET이라는 거대한 산업 인프라 위에서 ML 스타일 함수형 도메인 모델링을 한다"**는 데 있다. 이게 매력으로 다가오면 배울 가치가 충분하다.


19장 · 참고 / References

  • F# 9 발표 — devblogs.microsoft.com/dotnet/announcing-fsharp-9
  • F# 언어 레퍼런스 — learn.microsoft.com/dotnet/fsharp/language-reference
  • F# Foundation — fsharp.org
  • .NET 9 발표 — devblogs.microsoft.com/dotnet/announcing-dotnet-9
  • F# 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 블로그 — dsyme.github.io
  • Scott Wlaschin — Domain Modeling Made Functional — fsharpforfunandprofit.com
  • Krzysztof Cieslak — Ionide / Saturn — kcieslak.io