3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【脱ぬるぽ】代数的データ型のすすめ

Last updated at Posted at 2024-09-23

はじめに

最近学んだ代数的データ型が有用だったのでまとめます!
参考にした記事は以下の通りです

Java21とKotlinの代数的データ型 & パターンマッチの紹介と本当に嬉しい使い方

代数的データ型とは

代数的データ型とは、複数の型を組み合わせることで表現されるデータ型のことで、主に直積集合と直和集合によって表されます。

直積集合

直積集合は代数学におけるAND(かつ)と同義の集合です。いわば集合Aと集合Bの掛け算です。

A\times B = \lbrace (a,b) | a \in A, b\in B\rbrace

要するに構造体のことです。
よく使われる具体的な例として「名前」と「年齢」のデータ型があります。

cap.java
class Person{
    String name; //名前
    int age; //年齢
}

この時、名前の集合をA,年齢の集合をBとして、直積集合について以下で表現できます

\displaylines{
 \lbrace佐藤さん,鈴木さん、高橋さん \rbrace \in A \\
 \lbrace 21, 23, 26 \rbrace \in B \\

}
直積集合

このように全ての集合の組み合わせを積で表すことができます。

直和集合

直和集合は集合理論におけるOR(または)を表したものになります。(ただしAかつBは0です)

U = A \cup B \quad ( A \cap B = 0)

図で表すのこのような集合になります。(集合Aと集合Bで重なり合う部分がない集合)

直和集合

例として、Javaではsealed ~ permits ~ が直和を表現きます。

cup.java
sealed interface S 
    permits A,B,C{}

直和表現

S = A \oplus B \oplus C

この場合ならず集合Sの値はA or B or Cの集合に属することになります。

代数的データ型の表現

先ほどの直積集合と直和集合の組み合わせによって表現するのが代数的データ型です。
例えば、先ほどの直和表現の例で、集合Cの要素が「int型のnum」と「String型のstr」の値を保持するとすると、

S = A \oplus B \oplus (num \times str)

の式によって集合Sを表現することができます。
代数的データ型を使うメリットとして、取りうるデータの範囲を任意のデータ型で表現することができます。
ここまで式だけの説明では微妙なので実際の表現を見て行きましょう(ここからが本題)

実際の表現の仕方

実際に代数的データ型を活用する例を見て行きましょう。
タスク管理アプリについて考えます。

  • タスクの状態はInProgressとCompleted
  • タスクはタスク名「title」(String)を保持し、タスクの終了時刻「completedAt」(LocalDateTime)を保持する

代数的データ型を使用しない場合

TaskのクラスにはTitleとLocalDateTimeが存在することになります。

task1.java
class Task {
    String title;
    LocalDataTIme completedAt;
}

このTaskクラスを呼び出すときに、タスクまだ完了していない場合はTaskクラスのcompletedAtの持つ値はNullとなります。タスクが進行中の時は終了時刻を持っていないため、ぬるぽのリスクがあります。

代数的データ型を使用する場合

Task全体を代数的データ型と捉え、「InProgressTask」と「CompletedTask」の積和集合として表現する

Task = InProgressTask \oplus CompletedTask
task2.java
sealed interface Task 
    permits InProgressTask, CompletedTask{
    String title();
}

record InProgressTask(
    String title
) implements Task{}

record CompletedTask(
    String title,
    LocalDateTIme completedAt
) implements Task{}

このようにTaskを表現すると,タスクが進行中(InProgressTask)の時には終了時刻(completedAt)をそもそも持たないため、不要なぬるぽのリスクを避けることができます。
また、データ不整合があった場合、コンパイルフェーズで検出できます。

new InprogressTask("買い物");
new CompletedTask(
    "買い物",
    LocalDateTime.now()
);


//コンパイルエラー
new InProgressTask(
    "買い物",
    LocalDateTime.now()
);

おわりに

不要なぬるぽのリスクを代数的データ型を使って排除した方が将来的なリスク低減につながると思います!自身のコードでもしっかり取り入れて行きたいと思います!
拙い記事ですが最後まで読んでいただきありがとうございました!><
(意見・コメント・ご指摘などもよろしくお願いいたします!)

参考文献

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?