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
Kanon
June 06, 2025
Technology
3
1.3k
Java で学ぶ 代数的データ型
JJUG CCC 2025 Springの登壇資料です。
Kanon
June 06, 2025
Tweet
Share
More Decks by Kanon
See All by Kanon
Kotlinで学ぶ 代数的データ型
ysknsid25
5
1.1k
Type Challengesに新しい問題を追加して Type ChallengesのMaintainerになった話
ysknsid25
3
730
統計データで2024年の クラウド・インフラ動向を眺める
ysknsid25
2
1.2k
GAS × Discord bot × Gemini で作ったさいきょーの情報収集ツール
ysknsid25
1
1.6k
テストコード品質を高めるためにMutation Testingライブラリ・Strykerを実戦導入してみた話
ysknsid25
8
3.7k
そうだ、神戸へ行こう
ysknsid25
2
17k
テストコードの品質を客観的な数値で担保しよう〜Mutation Testのすすめ〜
ysknsid25
12
5.4k
「ばん・さく・つき・たー!」にならないためにSHIROBAKOから 学んだこと
ysknsid25
4
1.5k
東遊園地近辺のおすすめランチ・カフェ
ysknsid25
4
19k
Other Decks in Technology
See All in Technology
エンジニア向け技術スタック情報
kauche
0
110
強化されたAmazon Location Serviceによる新機能と開発者体験
dayjournal
2
150
知識を整理して未来を作る 〜SKDとAI協業への助走〜
yosh1995
0
130
活きてなかったデータを活かしてみた話 / Shirokane Kougyou vol 19
sansan_randd
1
400
AI技術トレンド勉強会 #1MCPの基礎と実務での応用
nisei_k
1
240
AIにどこまで任せる?実務で使える(かもしれない)AIエージェント設計の考え方
har1101
3
1.2k
新卒3年目の後悔〜機械学習モデルジョブの運用を頑張った話〜
kameitomohiro
0
370
生成AIでwebアプリケーションを作ってみた
tajimon
2
120
登壇ネタの見つけ方 / How to find talk topics
pinkumohikan
3
210
Абьюзим random_bytes(). Фёдор Кулаков, разработчик Lamoda Tech
lamodatech
0
270
IAMのマニアックな話 2025を執筆して、 見えてきたAWSアカウント管理の現在
nrinetcom
PRO
4
650
AWS Summit Japan 2025 Community Stage - App workflow automation by AWS Step Functions
matsuihidetoshi
1
140
Featured
See All Featured
Understanding Cognitive Biases in Performance Measurement
bluesmoon
29
1.8k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
137
34k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.8k
Mobile First: as difficult as doing things right
swwweet
223
9.7k
Bootstrapping a Software Product
garrettdimon
PRO
307
110k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
30
2.1k
Intergalactic Javascript Robots from Outer Space
tanoku
271
27k
Balancing Empowerment & Direction
lara
1
340
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.3k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
48
5.4k
Fantastic passwords and where to find them - at NoRuKo
philnash
51
3.3k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
10
920
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