Scala 言語仕様ガイド

他言語の経験者が 2〜3 時間でざっと読み通せるリファレンスです。関数型×OOP・型システム・並行処理など Scala 固有の概念を重点的に解説します。

Scala 3.x経験者向けコード例付き
01

変数・型・リテラル

基礎
val と var基礎

val はイミュータブル(再代入不可)、var はミュータブル(再代入可)。Scala では val を優先し、副作用を最小化するスタイルを推奨する。

Scala
val x: Int = 42       // 明示的型宣言
val y = "hello"       // 型推論 → String
var count = 0
count += 1            // OK
// x = 99             // コンパイルエラー
基本型基礎

Scala の型はすべてオブジェクト。Any が最上位で AnyVal(値型)と AnyRef(参照型)に分かれる。Nothing はすべての型のサブタイプで、例外を返す式の型に使われる。

Scala
val i: Int     = 42
val d: Double  = 3.14
val b: Boolean = true
val s: String  = "Scala"
val u: Unit    = ()      // void 相当
val n: Nothing = throw new Exception("!")
型推論基礎

Scala の型推論はローカル変数だけでなくメソッドの戻り値や高階関数の引数にも働く。ただし public なメソッドでは可読性のため明示的型宣言を推奨。

Scala
val list = List(1, 2, 3)   // List[Int]
val map  = Map("a" -> 1)   // Map[String, Int]
def double(n: Int) = n * 2 // 戻り値型: Int(推論)
タプル基礎

複数の値をまとめる軽量なデータ型。最大22要素まで。Scala 3 では (Int, String) のように書き、要素には ._1._2 でアクセスするか分割代入を使う。

Scala
val t = (42, "hello", true)
println(t._1)          // 42
val (n, s, b) = t      // 分割代入
println(s)             // hello
02

制御フロー

基礎
if 式基礎

Scala の if は文ではなく式であり、値を返す。三項演算子の代わりに if/else 式を使う。

Scala
val max = if a > b then a else b   // Scala 3 構文
val label = if score >= 60 then "Pass" else "Fail"
match 式基礎

match は Scala 最強の制御フロー構文。値・型・ガード・分割代入など多様なパターンに対応する。sealed との組み合わせで網羅性チェックが働く。

Scala
val result = x match
  case 0       => "zero"
  case n if n < 0 => s"negative: ${n}"
  case n       => s"positive: ${n}"
for 式 / for内包表記基礎

for ... yield で新しいコレクションを生成するモナド的内包表記。yield がない場合は副作用のループ。if ガードでフィルタリング可能。

Scala
val evens = for
  i <- 1 to 10
  if i % 2 == 0
yield i * i
// evens: Vector(4, 16, 36, 64, 100)
while / do-while基礎

命令型のループ。関数型スタイルでは while より再帰や Iterator を使う場面が多い。

Scala
var i = 0
while i < 5 do
  println(i)
  i += 1
03

関数とメソッド

基礎
def によるメソッド定義基礎

def でメソッドを定義。単一式なら = の後に直接書ける。再帰の場合は戻り値型の明示が必要。

Scala
def greet(name: String): String =
  s"Hello, ${name}!"

def factorial(n: Int): Int =
  if n <= 1 then 1 else n * factorial(n - 1)
デフォルト引数・名前付き引数基礎

デフォルト値を持つ引数を定義でき、呼び出し時に省略可能。名前付き引数で順序を変えて渡せる。

Scala
def connect(host: String, port: Int = 8080, ssl: Boolean = false): String =
  s"${if ssl then "https" else "http"}://${host}:${port}"

connect("example.com")                    // http://example.com:8080
connect("example.com", ssl = true)        // https://example.com:8080
高階関数基礎

関数を引数や戻り値として扱える。A => BFunction1[A, B] の糖衣構文。

Scala
def applyTwice(f: Int => Int, x: Int): Int = f(f(x))
val double: Int => Int = _ * 2
println(applyTwice(double, 3))  // 12
カリー化・部分適用基礎

複数の引数リストを持つメソッドを定義し、部分適用でカリー化された関数を作れる。

Scala
def add(a: Int)(b: Int): Int = a + b
val add5 = add(5)    // 部分適用 → Int => Int
println(add5(3))     // 8
04

コレクション

基礎
List と Vector基礎

List は連結リスト(先頭追加が O(1))。Vector はランダムアクセスが O(log n) の不変シーケンス。Seq は両方の親。

Scala
val list = List(1, 2, 3)
val vec  = Vector(1, 2, 3)
val prepended = 0 :: list        // List(0,1,2,3)
val appended  = vec :+ 4         // Vector(1,2,3,4)
Map と Set基礎

標準のコレクションはすべて不変(scala.collection.immutable)。変更時は新しいオブジェクトが返る。

Scala
val m = Map("a" -> 1, "b" -> 2)
val m2 = m + ("c" -> 3)   // 新しい Map
val s  = Set(1, 2, 3)
println(m.get("a"))        // Some(1)
println(m.getOrElse("x", 0)) // 0
map・filter・fold基礎

関数型スタイルでコレクションを変換する中核メソッド群。flatMap はネストを一段平坦化する。

Scala
val nums = List(1, 2, 3, 4, 5)
val doubled = nums.map(_ * 2)          // List(2,4,6,8,10)
val evens   = nums.filter(_ % 2 == 0) // List(2,4)
val sum     = nums.foldLeft(0)(_ + _) // 15
val flat    = List(List(1,2), List(3)).flatten // List(1,2,3)
LazyList と遅延評価基礎

LazyList は必要になるまで要素を評価しない遅延シーケンス。無限リストを表現できる。

Scala
val nats: LazyList[Int] = LazyList.from(1)
val first5 = nats.take(5).toList   // List(1,2,3,4,5)
val fibs: LazyList[BigInt] =
  BigInt(0) #:: BigInt(1) #:: fibs.zip(fibs.tail).map(_ + _)
05

文字列

基礎
文字列補間基礎

s"..." で変数/式を埋め込み、f"..." で printf スタイルのフォーマット、raw"..." でエスケープなし文字列を使う。

Scala
val name = "Scala"
val ver  = 3
println(s"Hello, ${name} ${ver}!")     // Hello, Scala 3!
println(f"Pi is ${math.Pi}%.4f")       // Pi is 3.1416
val path = raw"C:Usershiro"          // バックスラッシュそのまま
マルチライン文字列基礎

三重クォート """...""" でマルチライン文字列を定義。stripMargin で先頭の | を取り除ける。

Scala
val sql =
  """SELECT *
    |FROM users
    |WHERE active = true""".stripMargin
文字列操作基礎

Scala の String は Java の String と互換。Java のメソッドをそのまま呼べる他、Scala 独自の暗黙的拡張メソッドも使用できる。

Scala
val s = "  Hello, World!  "
println(s.trim.toLowerCase)       // hello, world!
println(s.contains("World"))      // true
val parts = "a,b,c".split(",")    // Array[String]
println("42".toInt + 1)           // 43
06

例外処理・Option・Either

基礎
try/catch/finally基礎

Java 互換の例外処理。ただし Scala では OptionEitherTry を使った純粋関数型スタイルを推奨。

Scala
try
  val n = "abc".toInt
catch
  case e: NumberFormatException => println(s"Error: ${e.getMessage}")
finally
  println("done")
Option[T]基礎

None または Some(value) を返す。null の代替として使い、getOrElsemapflatMap でチェーン処理できる。

Scala
val m = Map("a" -> 1)
val v: Option[Int] = m.get("a")   // Some(1)
val w = m.get("z")                // None
println(v.getOrElse(0))           // 1
val doubled = v.map(_ * 2)        // Some(2)
Either[L, R]基礎

成功(Right)か失敗(Left)のどちらかを表す。エラーメッセージ付きで失敗を扱いたい場合に Option より適する。

Scala
def parse(s: String): Either[String, Int] =
  s.toIntOption.toRight(s"'${s}' is not a number")

val result = for
  a <- parse("42")
  b <- parse("8")
yield a + b
// result: Right(50)
Try[T]基礎

例外を Success(value) または Failure(exception) としてラップ。try/catch を関数型スタイルで扱える。

Scala
import scala.util.{Try, Success, Failure}
val t = Try("123".toInt)
t match
  case Success(n) => println(s"Got ${n}")
  case Failure(e) => println(s"Error: ${e.getMessage}")
07

クラス・オブジェクト・case class

基礎
class基礎

class 定義でコンストラクタ引数を直接書ける。val/var を付けるとフィールドになる。

Scala
class Person(val name: String, var age: Int):
  def greet(): String = s"Hi, I'm ${name}, ${age} years old."

val p = Person("Alice", 30)
println(p.greet())
p.age = 31
case class基礎

case classequalshashCodetoStringcopy が自動生成される不変データクラス。パターンマッチと相性が良い。

Scala
case class Point(x: Double, y: Double)
val p1 = Point(1.0, 2.0)
val p2 = p1.copy(y = 5.0)   // Point(1.0, 5.0)
println(p1 == Point(1.0, 2.0)) // true(値比較)
object(シングルトン)基礎

object はシングルトンインスタンス。companion object(同名の object)にファクトリメソッドや定数を置く慣習がある。

Scala
object MathUtils:
  val Pi = 3.14159265
  def square(n: Double): Double = n * n

// companion object
class Circle(val r: Double)
object Circle:
  def apply(r: Double): Circle = new Circle(r)
val c = Circle(5.0)  // new 不要
enum(Scala 3)基礎

Scala 3 の enum は Java の enum より強力で、パラメータ付きの代数的データ型(ADT)を定義できる。

Scala
enum Color:
  case Red, Green, Blue

enum Shape:
  case Circle(radius: Double)
  case Rectangle(width: Double, height: Double)

val s: Shape = Shape.Circle(5.0)
s match
  case Shape.Circle(r)      => println(s"circle r=${r}")
  case Shape.Rectangle(w,h) => println(s"rect ${w}x${h}")
08

トレイト・継承

基礎
trait基礎

trait はインターフェースと抽象クラスの中間。デフォルト実装を持てる。複数の trait を with でミックスイン(多重継承)できる。

Scala
trait Greetable:
  def name: String
  def greet(): String = s"Hello, I'm ${name}"

trait Farewell:
  def bye(): String = "Goodbye!"

class Employee(val name: String) extends Greetable with Farewell

val e = Employee("Bob")
println(e.greet())   // Hello, I'm Bob
println(e.bye())     // Goodbye!
継承と override基礎

extends で継承し、override キーワードでメソッドをオーバーライド。super で親の実装を呼べる。

Scala
abstract class Animal:
  def sound(): String
  def describe(): String = s"I say ${sound()}"

class Dog extends Animal:
  override def sound(): String = "Woof"
  override def describe(): String = super.describe() + "!"

println(Dog().describe())  // I say Woof!
sealed trait / sealed class基礎

sealed を付けると同一ファイル内でしか継承できない。コンパイラがパターンマッチの網羅性を検証できる。

Scala
sealed trait Expr
case class Num(n: Int)           extends Expr
case class Add(l: Expr, r: Expr) extends Expr

def eval(e: Expr): Int = e match
  case Num(n)    => n
  case Add(l, r) => eval(l) + eval(r)
// 網羅チェック: ケースを追加するとコンパイル警告
09

ジェネリクス・型パラメータ

応用
型パラメータ基礎

[T] で型パラメータを宣言。<: で上限境界(T は A のサブタイプ)、>: で下限境界を指定できる。

Scala
def first[T](list: List[T]): Option[T] =
  if list.isEmpty then None else Some(list.head)

def max[T <: Comparable[T]](a: T, b: T): T =
  if a.compareTo(b) >= 0 then a else b
変位指定(Variance)応用

+T(共変)は F[Sub]F[Super] として使える。-T(反変)は逆。T のみは不変。List[+A] は共変なので List[Int]List[Any] に代入できる。

Scala
// List は共変(+A)
val ints: List[Int]  = List(1, 2, 3)
val anys: List[Any]  = ints  // OK

// Function1 は引数が反変(-T1)・戻り値が共変(+R)
val f: Int => Any    = (x: Int) => x.toString
given / using(Scala 3)応用

Scala 3 の given/using は Scala 2 の implicit の後継。型クラスや依存注入のパターンで使う。

Scala
trait Show[A]:
  def show(a: A): String

given Show[Int] with
  def show(n: Int): String = n.toString

def print[A](a: A)(using s: Show[A]): Unit =
  println(s.show(a))

print(42)  // 42
opaque type(Scala 3)応用

コンパイル時は別の型として扱われるが、実行時はラッパーオブジェクトが生成されない型エイリアス。型安全なドメイン型を zero-cost で作れる。

Scala
opaque type UserId = Int
object UserId:
  def apply(n: Int): UserId = n
  extension (id: UserId) def value: Int = id

val id = UserId(42)
// val n: Int = id  // コンパイルエラー(型安全)
println(id.value)   // 42
10

パターンマッチング

基礎
基本パターン基礎

リテラル・型・ワイルドカード(_)・ガード(if)などのパターンを組み合わせられる。

Scala
def describe(x: Any): String = x match
  case 0           => "zero"
  case n: Int      => s"int: ${n}"
  case s: String   => s"str: ${s}"
  case (a, b)      => s"pair: ${a}, ${b}"
  case _           => "other"
case class の分割代入基礎

case classunapply が自動生成されるため、パターンマッチで直接フィールドを取り出せる。

Scala
case class Point(x: Int, y: Int)
val p = Point(3, 7)
p match
  case Point(0, 0) => println("origin")
  case Point(x, 0) => println(s"on x-axis at ${x}")
  case Point(x, y) => println(s"(${x}, ${y})")
リストパターン基礎

:: で先頭と残りを分割する。再帰処理と組み合わせて使われる。

Scala
def sum(list: List[Int]): Int = list match
  case Nil     => 0
  case h :: t  => h + sum(t)

println(sum(List(1, 2, 3, 4)))  // 10
11

関数型プログラミング

基礎
純粋関数・参照透過性基礎

副作用(IO・変数書き換えなど)を持たず、同じ引数に対して常に同じ結果を返す関数。テストやリファクタリングが容易になる。

Scala
// 純粋関数(副作用なし)
def add(a: Int, b: Int): Int = a + b

// 非純粋(副作用あり)
var total = 0
def addImpure(n: Int): Int = { total += n; total }
Functor・Monad の概念応用

map で「文脈の中の値を変換」するのが Functor、flatMap でネストを避けながらチェーンするのが Monad。Scala の OptionListFuture はすべて Monad。

Scala
// Option Monad
val result = for
  x <- Some(10)
  y <- Some(20)
  z <- Some(x + y)
yield z * 2
// result: Some(60)
副作用の分離・IOの扱い応用

Cats Effect や ZIO では IO を値として扱い、副作用を型で表現することで純粋関数型プログラムを構成する。

Scala
// cats-effect の例(概念的)
import cats.effect.IO
val readLine: IO[String] = IO(scala.io.StdIn.readLine())
val printHello: IO[Unit] = IO(println("Hello!"))
val program: IO[Unit] = printHello >> readLine.flatMap(name => IO(println(s"Hi ${name}")))
末尾再帰 @tailrec基礎

@tailrec アノテーションを付けると末尾再帰をループに最適化。スタックオーバーフローを防ぐ。末尾位置にない場合はコンパイルエラーになる。

Scala
import scala.annotation.tailrec
@tailrec
def factorial(n: Int, acc: Int = 1): Int =
  if n <= 1 then acc else factorial(n - 1, n * acc)

println(factorial(10))  // 3628800
12

並行処理・Future

基礎
Future[T]基礎

Future[T] は非同期計算を表す。ExecutionContext 上で実行され、mapflatMap でコンビネーションできる。

Scala
import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.*

val f = Future { Thread.sleep(100); 42 }
val doubled = f.map(_ * 2)
println(Await.result(doubled, 1.second))  // 84
Future の結合基礎

Future.sequenceList[Future[A]]Future[List[A]] に変換。recover で失敗時のフォールバックを指定できる。

Scala
val f1 = Future(1)
val f2 = Future(2)
val both = for
  a <- f1
  b <- f2
yield a + b
// または
val all = Future.sequence(List(f1, f2))
Promise と Await基礎

PromiseFuture を手動で完了させるためのコンテナ。Await.result はブロッキング待機(本番では避ける)。

Scala
import scala.concurrent.Promise
val p = Promise[Int]()
val f = p.future
p.success(42)
println(Await.result(f, 1.second))  // 42
13

型クラス・Extension Methods

応用
型クラスパターン応用

型クラスは「特定の型に対して特定の操作を定義する」パターン。given で実装を提供し、using で受け取る。

Scala
trait Printable[A]:
  def format(a: A): String

given Printable[Int] with
  def format(n: Int): String = s"Int(${n})"

given Printable[String] with
  def format(s: String): String = s"Str(${s})"

def print[A: Printable](a: A): Unit =
  println(summon[Printable[A]].format(a))

print(42)        // Int(42)
print("hello")   // Str(hello)
Extension Methods(Scala 3)応用

extension キーワードで既存の型にメソッドを追加できる。Scala 2 の implicit class の後継。

Scala
extension (s: String)
  def shout: String = s.toUpperCase + "!"
  def whisper: String = s.toLowerCase + "..."

println("hello".shout)    // HELLO!
println("HELLO".whisper)  // hello...
given のインポート応用

given の実装は通常のインポートでは取り込めず、import Foo.given または import Foo.{given TypeClass} で明示的にインポートする。

Scala
// object MyGivens { given Ordering[String] = ... }
import MyGivens.given           // 全 given をインポート
import MyGivens.{given Ordering[?]} // 特定型の given のみ
14

ビルドツール・エコシステム

基礎
sbt基礎

Scala 標準のビルドツール。build.sbt でプロジェクト設定を記述。sbt compilesbt runsbt test でビルド・実行・テスト。

Scala
// build.sbt
scalaVersion := "3.4.0"
name := "my-project"

libraryDependencies ++= Seq(
  "org.typelevel" %% "cats-core" % "2.10.0",
  "org.scalatest" %% "scalatest" % "3.2.17" % Test
)
Cats / ZIO応用

cats は関数型の型クラス(Functor, Monad 等)を提供。cats-effect は IO モナドとランタイム。ZIO は型安全な副作用管理ライブラリ。

Scala
// ZIO の例(概念)
import zio.*
val program: ZIO[Any, Nothing, Unit] =
  ZIO.attempt(println("Hello ZIO")).orDie

// @main def run = ZIO.unsafe.run(program)
ScalaTest / MUnit基礎

ScalaTest は多様なスタイル(FlatSpec, FunSpec 等)のテストフレームワーク。MUnit は軽量でシンプルなテストライブラリ。

Scala
import org.scalatest.funsuite.AnyFunSuite
class MathSuite extends AnyFunSuite:
  test("addition"):
    assert(1 + 1 == 2)
  test("string"):
    assert("hello".length == 5)
15

Scala 3 の新機能

応用
Union 型 / Intersection 型応用

A | B は A または B のどちらかの型(Union 型)。A & B は A と B の両方を満たす型(Intersection 型)。Scala 3 の新機能。

Scala
def stringify(x: Int | String): String = x match
  case n: Int    => n.toString
  case s: String => s

// Intersection 型
trait Named { def name: String }
trait Aged  { def age: Int }
def greet(p: Named & Aged): String = s"${p.name} (${p.age})"
inline / マクロ応用

inline でコンパイル時に展開されるメソッドを定義できる。quotes/splices によるマクロ API でコード生成も可能。

Scala
inline def debug[A](expr: A): A =
  println(s"debug: ${expr}")
  expr

val x = debug(1 + 2)  // コンパイル時: println("debug: 3")
Match Types応用

型レベルのパターンマッチ。型引数によって戻り値型を変えられる。

Scala
type Elem[X] = X match
  case String      => Char
  case Array[t]    => t
  case Iterable[t] => t

val c: Elem[String]    = 'a'
val n: Elem[Array[Int]] = 42
16

Java 互換・エコシステム

応用
Java との相互運用応用

Scala は JVM 上で動作し、Java ライブラリをそのまま利用できる。Java の null に注意しつつ Option でラップするのが安全。

Scala
import java.util.{ArrayList, HashMap}
val list = new ArrayList[String]()
list.add("hello")
list.add("world")
println(list.size())  // 2

// Scala ↔ Java コレクション変換
import scala.jdk.CollectionConverters.*
val scalaList = list.asScala.toList
Scala.js / Scala Native応用

Scala.js は Scala コードを JavaScript にコンパイル。Scala Native は LLVM を使ってネイティブバイナリを生成。GC や JVM 不要で動作する。

Scala
// Scala.js: DOM 操作
import org.scalajs.dom
dom.document.getElementById("app").innerHTML = "<h1>Hello Scala.js!</h1>"
Apache Spark応用

Apache Spark の主要言語。Scala API は最もネイティブで高性能。大規模分散データ処理の標準的選択肢。

Scala
// Spark の例(概念)
val sc: SparkContext = ...
val rdd = sc.textFile("data.txt")
val words = rdd.flatMap(_.split(" "))
val counts = words.map(w => (w, 1)).reduceByKey(_ + _)
counts.saveAsTextFile("output")
ツールチェーン基礎

sbt が標準ビルドツール。Scala CLI はスクリプト実行に便利。REPL(scala コマンド)で対話的に試せる。Metals は主要な LSP サーバー。

Scala
// Scala CLI でスクリプト
// hello.sc
@main def hello() = println("Hello from Scala CLI!")
// $ scala-cli hello.sc
🏠