Union型とIntersection型
新しい型機能
「Union型とIntersection型」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。
新しい型の機能
Scala 3では、型システムにユニオン型(A | B)と交差型(A & B)が追加されています。継承階層を作らなくても、値をより正確に表現できます。
object Main:
def main(args: Array[String]): Unit =
println("Union: A | B, Intersection: A & B")ユニオン型の基本
ユニオン型 A | Bは、AまたはBのいずれかである値を表します。型に対する集合論的なORです。
object Main:
def show(x: Int | String): String = x match
case i: Int => s"int $i"
case s: String => s"str $s"
def main(args: Array[String]): Unit =
println(show(42))
println(show("hi"))ユニオン型に共通のスーパータイプは不要
Scala 2のEitherとは異なり、ユニオン型ではラッパーは必要ありません。共通の親クラスを持たない任意の2つの型をユニオンにできます。
object Main:
def parse(input: Int | Boolean): String =
input match
case n: Int => s"number $n"
case b: Boolean => s"flag $b"
def main(args: Array[String]): Unit =
println(parse(7))
println(parse(true))エラーのモデル化にユニオン型を使う
ユニオン型は、ラッパー型を使わずに軽量なエラー結果を表す場合に便利です。enumやシングルトンと組み合わせると、コードを簡潔に記述できます。
case object NotFound
object Main:
def lookup(id: Int): String | NotFound.type =
if id == 1 then "Alice" else NotFound
def main(args: Array[String]): Unit =
println(lookup(1))
println(lookup(2))交差型の基本
交差型 A & Bは、AでもありBでもある値を表します。型に対する集合論的なANDであり、traitの組み合わせによく使われます。
trait Named:
def name: String
trait Aged:
def age: Int
object Main:
def describe(p: Named & Aged): String =
s"${p.name} is ${p.age}"
def main(args: Array[String]): Unit =
val person = new Named with Aged:
def name = "Bob"
def age = 40
println(describe(person))交差型は可換的
交差型では順序は関係ありません。A & BはB & Aと同じ型です。これはScala 2の複合型A with Bとは異なり、後者はメンバー解決の一部で順序の影響を受けます。
trait Readable:
def read: String
trait Writable:
def write(s: String): Unit
object Main:
def use(r: Readable & Writable): Unit =
r.write("x")
println(r.read)
def main(args: Array[String]): Unit =
val rw = new Readable with Writable:
def read = "data"
def write(s: String) = println(s"wrote $s")
use(rw)複数のtraitを組み合わせる
3つ以上の型を交差させることもできます。その結果には、列挙したすべての機能が必要です。
trait A: def a: Int
trait B: def b: Int
trait C: def c: Int
object Main:
def sum(x: A & B & C): Int = x.a + x.b + x.c
def main(args: Array[String]): Unit =
val v = new A with B with C:
def a = 1
def b = 2
def c = 3
println(sum(v))ユニオン型の型エイリアス
長いユニオン型にはtypeエイリアスで名前を付け、シグネチャを読みやすくできます。
type Json = Int | String | Boolean
object Main:
def render(j: Json): String = j match
case i: Int => i.toString
case s: String => s"\"$s\""
case b: Boolean => b.toString
def main(args: Array[String]): Unit =
println(render("hi"))
println(render(true))ユニオン型の網羅的なマッチング
コンパイラーはユニオンのメンバーを把握しているため、マッチングがすべてのケースを網羅しているか確認できます。すべての選択肢を処理すれば、そのmatchは全域的になります。
type Shape = "circle" | "square"
object Main:
def sides(s: Shape): Int = s match
case "circle" => 0
case "square" => 4
def main(args: Array[String]): Unit =
println(sides("square"))ユニオン型の拡張
ユニオン型の値を型推論される変数に代入すると、Scalaはユニオン型を維持します。ただし、一部の操作では最小上界へ拡張されるため、ユニオン型を維持したい場合は型注釈を付けてください。
object Main:
def pick(flag: Boolean): Int | String =
if flag then 1 else "one"
def main(args: Array[String]): Unit =
val x: Int | String = pick(false)
println(x)使い分け
目的に合った機能を選びましょう。
- ユニオン:値が複数の型のいずれかである(OR)。
- 交差:値が複数のtraitを同時に満たす(AND)。
- ユニオンはラッパー型を、交差型は深い継承を避けるのに役立ちます。
trait Logger: def log(s: String): Unit
trait Clock: def now: Long
object Main:
def run(env: Logger & Clock): Unit =
env.log(s"time=${env.now}")
def main(args: Array[String]): Unit =
val env = new Logger with Clock:
def log(s: String) = println(s)
def now = 100L
run(env)理解度チェック
ユニオン型と交差型についての理解度を確認しましょう。
まとめ
Scala 3のユニオン型と交差型について学びました。
A | B:値はAまたはBで、ラッパーも共通の親も必要ありません。A & B:値はAでもBでもあり、可換的です。- パターンマッチングでユニオン型を絞り込み、交差型でtraitを組み合わせます。
typeエイリアスで長いユニオン型に名前を付けます。
object Main:
def length(x: String | List[Int]): Int = x match
case s: String => s.length
case l: List[Int] => l.length
def main(args: Array[String]): Unit =
println(length("hello"))
println(length(List(1, 2, 3)))よくある質問
「Union型とIntersection型」レッスンは無料ですか?
はい。「Union型とIntersection型」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。
「Union型とIntersection型」で何を学びますか?
新しい型機能 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「Union型とIntersection型」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?
はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 重要なインデント
- Scala 3 の enum
- Opaque Types
- Union型とIntersection型