필사 모드: 모던 F# 2026 — F# 9 (.NET 9) / Saturn / Giraffe / Bolero / Fable / Feliz / Elmish / Fantomas 심층 가이드
한국어프롤로그 — 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.Data`의 `JsonProvider`, `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`
현재 단락 (1/582)
매년 누군가는 묻는다. "F#, 아직도 써요?"