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.7k
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
5.9k
Kotlinで学ぶ 代数的データ型
ysknsid25
5
1.4k
Type Challengesに新しい問題を追加して Type ChallengesのMaintainerになった話
ysknsid25
3
960
統計データで2024年の クラウド・インフラ動向を眺める
ysknsid25
2
1.3k
GAS × Discord bot × Gemini で作ったさいきょーの情報収集ツール
ysknsid25
1
2k
テストコード品質を高めるためにMutation Testingライブラリ・Strykerを実戦導入してみた話
ysknsid25
8
3.9k
そうだ、神戸へ行こう
ysknsid25
2
17k
テストコードの品質を客観的な数値で担保しよう〜Mutation Testのすすめ〜
ysknsid25
12
5.8k
「ばん・さく・つき・たー!」にならないためにSHIROBAKOから 学んだこと
ysknsid25
4
1.6k
Other Decks in Technology
See All in Technology
AWS re:Invent 2025 で頻出の 生成 AI サービスをおさらい
komakichi
2
180
Quarkusで作るInteractive Stream Application
joker1007
0
160
組織の“見えない壁”を越えよ!エンタープライズシフトに必須な3つのPMの「在り方」変革 #pmconf2025
masakazu178
1
640
AI × クラウドで シイタケの収穫時期を判定してみた
lamaglama39
1
380
不確実性に備える ABEMA の信頼性設計とオブザーバビリティ基盤
nagapad
4
5.1k
【M3】攻めのセキュリティの実践!プロアクティブなセキュリティ対策の実践事例
axelmizu
0
180
re:Invent2025 事前勉強会 歴史と愉しみ方10分LT編
toshi_atsumi
0
240
国産クラウドを支える設計とチームの変遷 “技術・組織・ミッション”
kazeburo
4
6.4k
Building AI Applications with Java, LLMs, and Spring AI
thomasvitale
1
230
持続可能なアクセシビリティ開発
azukiazusa1
6
290
学術的根拠から読み解くNotebookLMの音声活用法
shukob
0
180
DDD x Microservice Architecture : Findy Architecture Conf 2025
syobochim
12
3.3k
Featured
See All Featured
Building an army of robots
kneath
306
46k
Reflections from 52 weeks, 52 projects
jeffersonlam
355
21k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
Unsuck your backbone
ammeep
671
58k
Done Done
chrislema
186
16k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.1k
Navigating Team Friction
lara
190
16k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
359
30k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
5.7k
Build The Right Thing And Hit Your Dates
maggiecrowley
38
2.9k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Faster Mobile Websites
deanohume
310
31k
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