Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
関数型プログラミングのメリットって何だろう?
Search
わんこ(Wanko_IT)
July 07, 2026
Programming
100
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
関数型プログラミングのメリットって何だろう?
わんこ(Wanko_IT)
July 07, 2026
More Decks by わんこ(Wanko_IT)
See All by わんこ(Wanko_IT)
バックエンドにElysiaJSを採用して気付いた、良い点・悪い点
wanko_it
1
340
安易なコード批判はやめよう
wanko_it
0
77
『リコリス・リコイル』に学ぶ!! 〜キャリア戦略における計画的偶発性理論と変わる勇気の重要性〜
wanko_it
2
1.1k
V8のJITコンパイルを完全に理解するLT
wanko_it
0
40
Other Decks in Programming
See All in Programming
AIキャラアプリkaiwaの低遅延音声通話基盤をどう作ったか - AWS Gravitonで支える低遅延・低コストAI Agent基盤
mogamit
0
150
技術記事、 専門家としてのプログラマ、 言語化
mizchi
14
7.2k
1B+ /day規模のログを管理する技術
broadleaf
0
120
気圧・高度・GPSを記録&可視化するアプリ「Koudo」を作った話
hjmkth
1
340
トークンをケチるな、設計しろ:GitHub Copilotを賢く使うコンテキスト戦略
ochtum
0
280
「なぜそう決めたのか」を残し続ける仕組み ― Notion AI カスタムエージェント × Slack連携による設計判断の自動記録 - NIKKEI Tech Talk #47
niftycorp
PRO
0
250
OS アップデート対応の取り組み方がもっと共有されてほしい
andpad
0
110
Mujeres en SEO Summit 2026 - Greatest Disaster Hits en Web Performance
guaca
0
230
A2UI という光を覗いてみる
satohjohn
1
170
エンジニアにデザインハーネスを 〜デザインプロセスを規定するためのハーネス〜 / Design harness from an engineer's perspective
rkaga
2
600
Observability in Practice:Grafana 與 Edge Device SRE 的那些事
blueswen
0
190
Language Server 使ってる? 〜VSCode と Zed の場合〜 / Are you using a Language Server? ~For VS Code and Zed~
handlename
0
820
Featured
See All Featured
Thoughts on Productivity
jonyablonski
76
5.2k
VelocityConf: Rendering Performance Case Studies
addyosmani
333
25k
The Limits of Empathy - UXLibs8
cassininazir
1
390
How Software Deployment tools have changed in the past 20 years
geshan
0
34k
Producing Creativity
orderedlist
PRO
348
40k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
330
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
55k
Discover your Explorer Soul
emna__ayadi
2
1.2k
Making Projects Easy
brettharned
120
6.7k
Transcript
関数型プログラミングの メリットってなんだろう? 清水日向 社内発表 / 10分
背景 私たちのチームでは関数型言語 Clojure を使って開発しています。日々の開発の中で「関数型プ ログラミングって、結局何が嬉しいんだろう?」と疑問に思い、調べた内容を共有します。 「なんとなく良さそう」で終わらせず、関数型プログラミングがどんなバグや問題を防いでくれ るのかを、実際のコードで確認していきます。難しい理論の話はしないので、気楽に聞いてくだ さい。 ※ コード例は
Clojure ではなく Java / Scala で書いています。括弧だらけの Clojure より読み慣れた構文のほうが、違いが伝 わりやすいので。
アジェンダ 01 関数型プログラミングとは何か 定義を確認し、本質が「不変性」にあることを押さえる 02 値の不変性とは何か 変数で表現する言語と、定数だけで表現する言語のコードを比べる 03 値が不変だと何が嬉しいのか 可変であることの問題2つ
— 共有ミュータブル状態・時間的結合 — をコードで見る 04 まとめ — なぜ合理的な選択なのか 「未知の未知」というソフトウェアの複雑性と不変性の関係
01 ─ 関数型プログラミングとは何か 関数型プログラミングの定義 そもそも、関数型プログラミングとは何でしょうか? 調べると、以下のような特徴が挙げられています。 参照透過性 同じ入力には常に同じ出力を 返す 値の不変性
一度作られた値は決して変更 されない 第一級関数 関数を値として扱い、引数や 戻り値にできる 高階関数の利用 関数を受け取る・返す関数を 活用するスタイル …ただし、広く認められた共通の定義は確立されていない。そこで今回は、ある定義を採用します。
01 ─ 関数型プログラミングとは何か 関数型プログラミングとは、変数のないプログラミングと言え るだろう。関数型プログラムの値は変化しないのである。 『関数型デザイン』Robert C. Martin 今回はこの定義を採用します。つまり、関数型プログラミングの本質は「不変性」に ある。
02 ─ 値の不変性とは何か 値の不変性 Java・C・PHP・Ruby などのオブジェクト指向・手続き型の言語は変数(変更可能な値)で状態を表現する。一方で Clojure・Scala・Erlang などの関数型志向の言語は、定数(不変な値)を基本とする。 非関数型(Java)— 変数を書き換える
class Wallet { private int money; public Wallet(int money) { this.money = money; } public int getMoney() { return money; } public void addMoney(int amount) { money = money + amount; // 中身を書き換える } } Wallet wallet = new Wallet(1000); wallet.getMoney(); // 1000 wallet.addMoney(500); wallet.getMoney(); // 1500 ← 状態が変わった 関数型(Scala)— 新しい値を作る case class Wallet(money: Int) def addMoney(wallet: Wallet, amount: Int): Wallet = Wallet(wallet.money + amount) // 新しい財布を作る val wallet1 = Wallet(1000) val wallet2 = addMoney(wallet1, 500) wallet1.money // 1000 ← 変わっていない! wallet2.money // 1500 ← 新しくできた財布 関数型では、値は定数が基本。状態の変化は「既存の値をもとに作られた新しい定数」として表現する。
値が不変だと、何が嬉しい? 逆から考える — 値が可変であることに伴う問題は2つある 問題 ① ── いまから見る 共有ミュータブル状態 同じインスタンスへの変更が、予期せぬ場所へ波及する
問題 ② 時間的結合 変更の途中に「不正な中間状態」が存在してしまう
03 ─ 値が不変だと何が嬉しいのか 問題① 共有ミュータブル状態(Java) Q. このコード、何が出力されるでしょう? 月末ごとの残高を記録するクラス class BalanceHistory
{ // 特定地点での財布の状態を保持 private List<Wallet> snapshots = new ArrayList<>(); // 「今この瞬間の状態」を記録 public void recordSnapshot(Wallet wallet) { snapshots.add(wallet); } // 記録された履歴を取得 public List<Wallet> getSnapshots() { return snapshots; } } 利用側のコード Wallet wallet = new Wallet(1000); BalanceHistory history = new BalanceHistory(); history.recordSnapshot(wallet); // 1月末: 1000円を記録 wallet.addMoney(500); history.recordSnapshot(wallet); // 2月末: 1500円を記録 wallet.addMoney(-1500); history.recordSnapshot(wallet); // 3月末: 0円を記録 for (Wallet snap : history.getSnapshots()) { System.out.println(snap.getMoney()); }
03 ─ 値が不変だと何が嬉しいのか 予想と異なる出力 直感的な予想 1000 1500 0 各月末に記録した瞬間の残高が、そのまま残っているは ず…
実際の出力 0 0 0 1月末の「1000円」も2月末の「1500円」も消えてしまっ た
03 ─ 値が不変だと何が嬉しいのか 原因: 3要素は同一インスタンス recordSnapshot(Wallet wallet) の引数は参照渡し。リストに追加されるのはコピーではなく、同じWalletへの 参照。3要素が指す実体は1つで、その値は最後の変更(3月末=0円)の状態になっている。 ①
リストの中身を図にすると snapshots[0] ──▶ snapshots[1] ──▶ snapshots[2] ──▶ Wallet(money = 0) たった1つの同じインスタンス ② コードで要素の等価性を確認しても、同一 snapshots.get(0) == snapshots.get(1) // true snapshots.get(0) == snapshots.get(2) // true snapshots.get(1) == snapshots.get(2) // true 共有ミュータブル状態では、処理の影響が予期せぬ形で波及する。アプリ全体を正常に保つために「考えるべき こと」が増大してしまう。
03 ─ 値が不変だと何が嬉しいのか 不変な値による解決(Scala) case class BalanceHistory(snapshots: List[Wallet] = Nil)
{ def recordSnapshot(wallet: Wallet): BalanceHistory = BalanceHistory(snapshots :+ wallet) } // 1月末: 1000円 val wallet1 = Wallet(1000) val history1 = BalanceHistory().recordSnapshot(wallet1) // 2月末: 1500円 val wallet2 = wallet1.addMoney(500) val history2 = history1.recordSnapshot(wallet2) // 3月末: 0円 val wallet3 = wallet2.addMoney(-1500) val history3 = history2.recordSnapshot(wallet3) history3.snapshots.foreach(w => println(w.money)) // 1000, 1500, 0 ← 期待どおり! ポイント 1 プログラム中のすべての値は変更不能かつ 異なる実体であると、言語が保証する ポイント 2 異なる状態のWalletは別々の実体として保 存され、後から変更される心配がない ポイント 3 「スナップショット」が言葉どおりの意味 で機能し、記録した瞬間の状態がそのまま 残る 出力: 1000 → 1500 → 0 ✓
値が可変であることの問題、もう1つ 今度は「空間」ではなく「時間」に潜む問題 問題 ② ── いまから見る 時間的結合 変更の途中に「不正な中間状態」が存在してしまう 問題 ①
── 解決 ✓ 共有ミュータブル状態 不変な値なら、記録した状態が後から変わることはない
03 ─ 値が不変だと何が嬉しいのか 問題② 時間的結合(Java) class Wallet { private int
balance = 1000000; // 残高(100万円) private int count = 0; // 送金回数 // 送金を行うメソッド public void transfer(int amount) { balance -= amount; // ← この瞬間「残高だけ減って回数は0のまま」 // という業務上ありえない財布が存在する count += 1; } } balance と count はセットで変わるべき値だが、 変数は1つずつしか書き換えられないため、途中に 不正な中間状態が実在する瞬間が生まれる。 その結果「この瞬間に参照・操作してはいけない」 という時間軸上の制約が生まれる。コードのどこ にも書かれていないこの依存が時間的結合。 この一瞬を別スレッドが読み取れば再現困難なバグに。 あいだで例外が起きれば不整合なまま残り続ける。 transfer() 実行中の時間軸 balance -= amount; ▶ 不正な中間状態 残高: 99万円 / 送金回数: 0回 ▶ count += 1;
03 ─ 値が不変だと何が嬉しいのか 同時に確定する状態(Scala) case class Wallet(balance: Int, count: Int)
{ def transfer(amount: Int): Wallet = Wallet(balance - amount, count + 1) // ↑ 残高と回数が同時に確定した新しい財布 } val w0 = Wallet(1000000, 0) val w1 = w0.transfer(10000) // w0: (1000000, 0) … 送金前の財布 // w1: ( 990000, 1) … 送金後の財布 // 「残高だけ減った財布」は書きようがない 不変な値では、状態の変化を全フィールドが同時 に確定した新しい値としてしか表現できない。 つまり不正な中間状態が、そもそも実在しない。 存在するのは送金前と送金後、2つの正しい財布 だけ。 中間状態が消えれば「この瞬間に参照・操作して はいけない」という制約も丸ごと消える — 守るべ きルールではなく、ルールが必要な状況自体をな くす。 可変(Java) 1つの財布が時間とともに姿を変え、途中の不正な姿 が存在してしまう。 不変(Scala) 存在するのは w0 と w1 という2つの完成品だけ。途 中がない。
04 ─ まとめ: なぜこれが重要か “Of the three manifestations of complexity,
unknown unknowns are the worst.” John Ousterhout『A Philosophy of Software Design, 2nd Edition』 ソフトウェアの複雑性のうち最悪なのが「未知の未知」— 問題が潜んでいるかどうかさえ、知る術がないこ と。バグが出たとき、初めてその存在に気づく。 ここまで見てきた可変な値に起因するバグは、特定の手順・条件でしか起きない。そのため動作検証やテス トをすり抜け、バグが顕在化するまで問題の存在に気づけない — 未知の未知そのものである。 不変な値なら、不整合な状態はそもそも作れない。「未知の未知」になる前に、言語が芽を摘んでくれる。
04 ─ まとめ まとめ 関数型プログラミングとは、不正な状態を発生させにくくするプログラミングスタイルで ある。 1 本質は不変性 — あらゆる値を定数として扱う
状態の変化は「既存の値をもとにした新しい定数」として表現する 2 共有ミュータブル状態と時間的結合を構造的に排除できる 予期せぬ波及も、不正な中間状態も、そもそも表現できない 3 銀の弾丸ではないが、合理的な選択肢 再現困難でテストをすり抜ける「未知の未知」型バグの発生確率を下げられる ご清聴ありがとうございました