Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Step 1: 型パラメータは契約である

1-1. 自分だけの Box を作る

// step1a.scala

// A box that holds anything (no type parameter)
class AnyBox(val value: Any)

// A box with a type parameter
class Box[A](val value: A)

@main def step1(): Unit =
  // AnyBox: easy to put things in
  val anyBox = AnyBox(42)
  // val n: Int = anyBox.value  // Compile error! Any is not Int
  val n: Int = anyBox.value.asInstanceOf[Int]  // Dangerous cast

  // Box[A]: the type is preserved
  val intBox = Box(42)       // Inferred as Box[Int]
  val m: Int = intBox.value  // Comes out as Int — no cast needed

  // Wrong type? Compile error.
  // val s: String = intBox.value  // error: Found Int, Required String

  println(s"AnyBox: $n, Box[Int]: $m")

試してみよう:

> scala-cli run step1a.scala

次に val s: String = intBox.value のコメントを外してみてほしい。

ポイント: Box[A]A は中に入れたものを「覚えている」のではない。 コンパイラは構築時に A = Int確定する。これが契約だ。 値を取り出すとき、それは Int であることが保証される。

1-2. 関数の型パラメータ ― 契約は伝播する

// step1b.scala

def first[A](xs: List[A]): A = xs.head

@main def step1b(): Unit =
  val names = List("Scala", "Rust", "Go")
  val top: String = first(names)  // Compiler resolves A = String
  println(top)

  val nums = List(1, 2, 3)
  val one: Int = first(nums)      // Compiler resolves A = Int
  println(one)

  // What about this?
  val mixed = List(1, "two", 3.0)
  val what = first(mixed)         // What is A here?
  println(what.getClass)

型を確認してみよう:

scala-cli run step1b.scala

# 推論された型を見る(コンパイルキャッシュを回避するため先にクリーン):
scala-cli clean step1b.scala && scala-cli compile step1b.scala -O -Xprint:typer 2>&1 | grep "mixed"

mixedList[Int | String | Double] と推論される ― これはユニオン型で、Scala 3 の機能だ。

List[Int | String | Double] とは一度も書いていないことに注目してほしい ― コンパイラが推論したのだ。 本書を通じて、コンパイラが文脈から型パラメータを導き出す能力に頼る。 型推論が内部的にどう動くかはそれ自体が一つのテーマだが、ここでは単にそれが機能すると信頼し、 型が何を意味するかに集中する。