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
1.9k
4
Share
Java で学ぶ 代数的データ型
JJUG CCC 2025 Springの登壇資料です。
301 Moved Permanently
June 06, 2025
More Decks by 301 Moved Permanently
See All by 301 Moved Permanently
TypeScript 上達の道
ysknsid25
24
6.3k
Kotlinで学ぶ 代数的データ型
ysknsid25
5
1.6k
Type Challengesに新しい問題を追加して Type ChallengesのMaintainerになった話
ysknsid25
3
1.1k
統計データで2024年の クラウド・インフラ動向を眺める
ysknsid25
2
1.3k
GAS × Discord bot × Gemini で作ったさいきょーの情報収集ツール
ysknsid25
1
2.2k
テストコード品質を高めるためにMutation Testingライブラリ・Strykerを実戦導入してみた話
ysknsid25
8
4.1k
そうだ、神戸へ行こう
ysknsid25
2
17k
テストコードの品質を客観的な数値で担保しよう〜Mutation Testのすすめ〜
ysknsid25
12
6.1k
「ばん・さく・つき・たー!」にならないためにSHIROBAKOから 学んだこと
ysknsid25
4
1.7k
Other Decks in Technology
See All in Technology
Babylon.js Japan Activities (2026/4)
limes2018
0
190
マルチモーダル非構造データとの闘い
shibuiwilliam
1
290
Databricks Lakebaseを用いたAIエージェント連携
daiki_akimoto_nttd
0
170
Oracle AI Database@Azure:サービス概要のご紹介
oracle4engineer
PRO
6
1.4k
AIペネトレーションテスト・ セキュリティ検証「AgenticSec」ご紹介資料
laysakura
0
1.6k
解剖"React Native"
hacusk
0
120
Claude Teamプランの選定と、できること/できないこと
rfdnxbro
1
1.7k
プロダクトを触って語って理解する、チーム横断バグバッシュのすすめ / 20260411 Naoki Takahashi
shift_evolve
PRO
1
210
BIツール「Omni」の紹介 @Snowflake中部UG
sagara
0
240
ふりかえりを 「あそび」にしたら、 学習が勝手に進んだ / Playful Retros Drive Learning
katoaz
0
400
Cortex Codeでデータの仕事を全部Agenticにやりきろう!
gappy50
0
320
OCI技術資料 : 証明書サービス概要
ocise
1
7.2k
Featured
See All Featured
Automating Front-end Workflow
addyosmani
1370
200k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
1
500
Skip the Path - Find Your Career Trail
mkilby
1
100
Building a Scalable Design System with Sketch
lauravandoore
463
34k
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.2k
Git: the NoSQL Database
bkeepers
PRO
432
67k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
260
Reality Check: Gamification 10 Years Later
codingconduct
0
2.1k
Deep Space Network (abreviated)
tonyrice
0
110
Writing Fast Ruby
sferik
630
63k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.6k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
100
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