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
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
49
Other Decks in Programming
See All in Programming
仕様駆動開発の消費期限
watany
15
7k
【やさしく解説 設計編・中級 #4】ルールの寿命と、システムの年輪
panda728
PRO
2
180
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
590
「人を評価する AI」の設計と実装
ryoyanara
0
180
20260722_microCMSで考える、AI時代のコンテンツ運用設計
yosh1
0
370
AI Engineeringは、AIプロダクトだけのものか? 〜AIがソフトウェアを作る時代の新しい当たり前〜 / No AI in your product. AI Engineering in your development.
rkaga
4
380
生成AIで帳票OCRが「簡単に」作れる時代になった?
kon_shou
0
590
その節約、円になってますか?
isamumumu
1
700
Claude CodeとAgentCore Gatewayを繋ぐ際の認証認可 / Authentication and authorization when connecting Claude Code with AgentCore Gateway
har1101
1
250
ITヒヤリハットを整理してみた ~ライフサイクルと原因から考える再発防止策~
koukimiura
1
140
Android CLI
fornewid
0
210
継続モナドとリアクティブプログラミング
yukikurage
3
690
Featured
See All Featured
Technical Leadership for Architectural Decision Making
baasie
3
470
Agile that works and the tools we love
rasmusluckow
331
22k
Optimising Largest Contentful Paint
csswizardry
37
3.9k
Amusing Abliteration
ianozsvald
1
240
Product Roadmaps are Hard
iamctodd
55
12k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
540
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
780
Facilitating Awesome Meetings
lara
57
7.1k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.9k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
230
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
450
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
5
1.2k
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 銀の弾丸ではないが、合理的な選択肢 再現困難でテストをすり抜ける「未知の未知」型バグの発生確率を下げられる ご清聴ありがとうございました