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
Java で学ぶ 代数的データ型
Search
301 Moved Permanently
June 06, 2025
Technology
4
1.9k
Java で学ぶ 代数的データ型
JJUG CCC 2025 Springの登壇資料です。
301 Moved Permanently
June 06, 2025
Tweet
Share
More Decks by 301 Moved Permanently
See All by 301 Moved Permanently
TypeScript 上達の道
ysknsid25
24
6.1k
Kotlinで学ぶ 代数的データ型
ysknsid25
5
1.5k
Type Challengesに新しい問題を追加して Type ChallengesのMaintainerになった話
ysknsid25
3
1.1k
統計データで2024年の クラウド・インフラ動向を眺める
ysknsid25
2
1.3k
GAS × Discord bot × Gemini で作ったさいきょーの情報収集ツール
ysknsid25
1
2.1k
テストコード品質を高めるためにMutation Testingライブラリ・Strykerを実戦導入してみた話
ysknsid25
8
4k
そうだ、神戸へ行こう
ysknsid25
2
17k
テストコードの品質を客観的な数値で担保しよう〜Mutation Testのすすめ〜
ysknsid25
12
6k
「ばん・さく・つき・たー!」にならないためにSHIROBAKOから 学んだこと
ysknsid25
4
1.7k
Other Decks in Technology
See All in Technology
Ruby版 JSXのRuxが気になる
sansantech
PRO
0
160
マーケットプレイス版Oracle WebCenter Content For OCI
oracle4engineer
PRO
5
1.6k
SRE Enabling戦記 - 急成長する組織にSREを浸透させる戦いの歴史
markie1009
0
140
学生・新卒・ジュニアから目指すSRE
hiroyaonoe
2
670
日本の85%が使う公共SaaSは、どう育ったのか
taketakekaho
1
230
AIエージェントを開発しよう!-AgentCore活用の勘所-
yukiogawa
0
180
外部キー制約の知っておいて欲しいこと - RDBMSを正しく使うために必要なこと / FOREIGN KEY Night
soudai
PRO
12
5.6k
Red Hat OpenStack Services on OpenShift
tamemiya
0
120
What happened to RubyGems and what can we learn?
mikemcquaid
0
310
AIエージェントに必要なのはデータではなく文脈だった/ai-agent-context-graph-mybest
jonnojun
0
170
GitHub Issue Templates + Coding Agentで簡単みんなでIaC/Easy IaC for Everyone with GitHub Issue Templates + Coding Agent
aeonpeople
1
250
顧客の言葉を、そのまま信じない勇気
yamatai1212
1
360
Featured
See All Featured
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
190
My Coaching Mixtape
mlcsv
0
48
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
270
Evolving SEO for Evolving Search Engines
ryanjones
0
130
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
180
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
920
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
55k
A Soul's Torment
seathinner
5
2.3k
Making the Leap to Tech Lead
cromwellryan
135
9.7k
Crafting Experiences
bethany
1
49
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
330
How to build a perfect <img>
jonoalderson
1
4.9k
Transcript
Kanon #jjug_ccc Java で学ぶ 代数的データ型 ysknsid25 ysknsid25.bsky.social
⛳ このセッションのゴール ⛳ 2 • 代数的データ型とは何かを理解できる • 代数的データ型をJavaで表現することができるようになる
関数型プログラミングといえば? な キーワードを 1つ想像してください 3
4 あなたが想像したのはこれですね? 純粋関数
5 純粋関数とは • 戻り値は常に一つ • 関数は引数にのみ基づいて戻り値を計算する • 関数は既存の値を変更しない
6 ではこういう純粋関数はどうだろう? public static String activityStatus(Date latestLoginDate, Date referenceDate) {
if (latestLoginDate == null) { return "INACTIVE"; } Calendar calendar = Calendar.getInstance(); calendar.setTime(referenceDate); calendar.add(Calendar.YEAR, -1); Date oneYearAgo = calendar.getTime(); return latestLoginDate.after(oneYearAgo) ? "ACTIVE" : "INACTIVE"; } 戻り値は1つの文字列で 引数にのみ基づいてるし 既存の値は変えてない 例なので結構極端 最終ログインが 1年前ならINACTIVE、1年以内ならActiveとする
7 先の関数を呼び出す public static void main (String[] args) { //
do something… String status = activityStatus(latestLoginDate, referenceDate) // do something… } INACTIVE ACTIVE 実装を見ないと 振る舞いがわからない 引数はこの組み合わせ意味 (活動中かどうかの条件 )を 持っているが それは実装を見ないとわから ない
8 純粋関数はなんのため? 関数の宣言を見て振る舞いが想像できる & 期待どおりに振る舞う 代数的データ型が役立つ!!
代数的データ型 9
代数的データ型 = 直積型 & 直和型 10 また知らないワード…
11 直積型 代数的データ型 • ここでは 星座 × 年齢。A × B
× C … 掛け算なので直積 • JavaでいうとRecordやBean Class • 要はデータクラス 年齢 星座 (蟹, 20) (双子, 30) (射手, 40) (山羊, 20) public record PersonInfo( String constellation, int age ){}
12 直和型 代数的データ型 • ここでは 星座 は必ず12個で、牡牛座であり牡牛座であることはありえない。 • 和集合 A
∪ B ただし A ∩ B = φ … 直和 • Javaでいうとenumやsealed class 星座 public enum Constellation { ARIES, // おひつじ座 TAURUS, // おうし座 GEMINI, // ふたご座 CANCER, // かに座 LEO, // しし座 VIRGO, // おとめ座 LIBRA, // てんびん座 SCORPIO, // さそり座 SAGITTARIUS, // いて座 CAPRICORN, // やぎ座 AQUARIUS, // みずがめ座 PISCES // うお座 } 牡羊 牡牛 双子 蟹 乙女 天秤 蠍 射手 山羊 水瓶 魚 獅子
代数的データ型 = 直積型 & 直和型 13 改めて
代数的データ型が ないとき 14
15 代数的データ型になっていないコード 代数的データ型 import java.util.Optional; public record PeriodInYears(int start, Optional<Integer>
end){}; public static void main(String[] args) { PeriodInYears p1 = new PeriodInYears(1981, Optional.empty()); PeriodInYears p2 = new PeriodInYears(1968, Optional.of(1980)); System.out.println(p1); // PeriodInYears[start=1981, end=Optional.empty] System.out.println(p2); // PeriodInYears[start=1968, end=Optional[1980]] } 活動期間を表現したい endが存在しない = 活動中 endが存在する = 活動終了 と表現できないだろうか? 活動中なのに活動終了と判定され る間違いも起きそう 直積の概念のみが反映された状態のコード
16 代数的データ型が あるとき
17 代数的データ型になったコード 代数的データ型 // 直和型 public sealed interface YearsActive permits
StillActive, ActiveBetween {} // record(直積)×sealed(直和)の代数的データ型 public record StillActive(int since) implements YearsActive { /** 妥当性の検証は省略 */ } public record ActiveBetween(int start, int end) implements YearsActive { /** 妥当性の検証は省略 */ } public static void main(String[] args) { YearsActive y1 = new StillActive(2005); YearsActive y2 = new ActiveBetween(1990, 2000); System.out.println(y1); // StillActive[since=2005] System.out.println(y2); // ActiveBetween[start=1990, end=2000] } データの意味が一目でわかるようになったし、間違いも起こりにくそう
18 sealedではなくenumじゃだめなの? 代数的データ型 public enum YearsActive { STILL_ACTIVE { private
int since; @Override void setData(int a, int b) { this.since = a; } }, ACTIVE_BETWEEN { private int start; private int end; @Override void setData(int a, int b) { this.start = a; this.end = b; } }; abstract void setData(int a, int b); } enumで直積の性質を表現するのは無理がある。 以下のようにだいぶ無理があるコードができあがる。int b を無理やり入れないとだし、どう考えてもスマートじゃない public static void main(String[] args) { YearsActive y1 = YearsActive.STILL_ACTIVE; y1.setData(2005, 9999); // 9999ってなんだ・・・ YearsActive y2 = YearsActive.ACTIVE_BETWEEN; y2.setData(1990,2000); System.out.println(y1); // STILL_ACTIVE System.out.println(y2); // ACTIVE_BETWEEN }
19 継承じゃだめなの? 代数的データ型 sealedではなく継承を使ってしまうと、理論上無限の集合が出来上がる = 直和の性質がない YearsActive StillActive ActiveBetween PreviousLife
知らないところで勝手に前世を定義できてしまう もちろん来世も定義できる
20 さらなる恩恵を受ける 代数的データ型 // 直和型 public sealed interface YearsActive permits
StillActive, ActiveBetween {} // record(直積)×sealed(直和)の代数的データ型 public record StillActive(int since) implements YearsActive { /** 妥当性の検証は省略 */ } public record ActiveBetween(int start, int end) implements YearsActive { /** 妥当性の検証は省略 */ } public static void main(String[] args) { YearsActive y1 = new StillActive(2005); YearsActive y2 = new ActiveBetween(1990, 2000); System.out.println(y1); // StillActiveならsinceだけ出力 System.out.println(y2); // ActiveBetweenは開始と終了を出力 } System.out.println()の内容をデータによって切り分ける関数を作るとする
21 代数的データ型の威力 代数的データ型 public sealed interface YearsActive permits StillActive, ActiveBetween
{} public record StillActive(int since) implements YearsActive {} public record ActiveBetween(int start, int end) implements YearsActive {} public static void main(String[] args) { YearsActive y1 = new StillActive(2005); YearsActive y2 = new ActiveBetween(1990, 2000); System.out.println(getYearsMessage(y1)); System.out.println(getYearsMessage(y2)); } static String getYearsMessage(YearsActive ya) { return switch (ya) { case StillActive sa -> "Still active since " + sa.since(); case ActiveBetween ab -> "Active between " + ab.start() + " and " + ab.end(); }; } • シグネチャから処理が明らかになった • 引数として必要なデータが何か?も明らか • データの不整合が起こり得ない ◦ 先のenumでみた9999とか • YearsActiveでとりうる値の範囲が限定され ることで、switchにデフォルトが生えない ◦ コンパイル時点でデータの整合性が担 保される • 逆にYearsActiveに新たな値が増えた場合 は、getYearsMessage でエラーが出るので修 正漏れが出ない
22 まとめ • 代数的データ型 = 直積型 & 直和型 ◦ enumは直積の性質がない
◦ 継承は直和の性質がない • 代数的データ型を使うことでより明示的なシグネチャを定義できる • 代数的データ型を使うことで取りうるデータの範囲を型で表現できる • 代数的データ型はコンパイル時点での品質保証に寄与する
Happy Hacking !! 水瀬いのり さんが推し の @ysknsid25 @ysknsid25.bsky.social Presented by
Kanon でした
24 • これまでの`YearsActive`データモデルを実際に定義しましょう • `master`ブランチにリファクタ前のコードがあります • `answer`ブランチはリファクタ後のコードです。さっきまで出していたサン プルと同じ内容になっています • 今学んだ代数的データ型を定義し、`master`ブランチのテストコードが通
るように修正してみてください 実際に代数的データ型を書いてみよう! 時間が余れば https://github.com/ysknsid25/jjug-spring-2025