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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
わんこ(Wanko_IT)
July 07, 2026
Programming
210
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
390
安易なコード批判はやめよう
wanko_it
0
79
『リコリス・リコイル』に学ぶ!! 〜キャリア戦略における計画的偶発性理論と変わる勇気の重要性〜
wanko_it
2
1.2k
V8のJITコンパイルを完全に理解するLT
wanko_it
0
47
Other Decks in Programming
See All in Programming
ドリフトを絶対に許さない(?)CDK運用 / CDK Ops with Zero Tolerance for Drifts (?)
akihisaikeda
1
170
ソフトウェア設計に溶けるインフラ ― AWS CDK のインフラ認識論
konokenj
3
750
メールのエイリアス機能を履き違えない
isshinfunada
0
220
使用 Meilisearch 建立新聞搜尋工具
johnroyer
0
210
仕様駆動開発の消費期限
watany
7
1.4k
AI時代、エンジニアはどう育つのか -未経験エンジニアの成長を間近で見て考えたこと-
thasu0123
0
210
【SRE NEXT 2026 Lunch Session】一人目専任SREの立ち上げを加速する ― AIと進めたオンボーディングで2分を0.04秒にした話
pkshadeck
PRO
0
3.5k
ルールを書いて終わらせないハーネスエンジニアリング
yug1224
4
1.8k
全PRの83%がAIレビューだけでマージできるようになった開発組織はその後どうなったか
athug
1
1.4k
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
310
GDG Korea Android: 2026 I/O Extended ~ What's new in Android development tools
pluu
0
210
Claude Team Plan導入・ガイド
tk3fftk
0
250
Featured
See All Featured
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
360
WCS-LA-2024
lcolladotor
0
790
KATA
mclloyd
PRO
35
15k
Fashionably flexible responsive web design (full day workshop)
malarkey
408
67k
Crafting Experiences
bethany
1
240
YesSQL, Process and Tooling at Scale
rocio
174
15k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
4.2k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Designing Powerful Visuals for Engaging Learning
tmiket
1
470
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
760
Designing Dashboards & Data Visualisations in Web Apps
destraynor
232
55k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
400
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 銀の弾丸ではないが、合理的な選択肢 再現困難でテストをすり抜ける「未知の未知」型バグの発生確率を下げられる ご清聴ありがとうございました