Scala
#26
TIOBE#31
PYPL#22
GitHub#70
RedMonk#14
IEEESpectrum#22
JetBrains#19
プログラミング言語
Scala
概要
Scalaは、関数型プログラミングとオブジェクト指向プログラミングを融合したJVM上で動作するマルチパラダイムプログラミング言語です。
詳細
Scala(Scalable Language)は2003年にMartin Odersky氏によって開発されたプログラミング言語で、関数型プログラミングとオブジェクト指向プログラミングの両方のパラダイムを統合し、Java Virtual Machine(JVM)上で動作します。静的型付けでありながら型推論により簡潔な記述が可能で、不変性を重視した設計、高階関数、パターンマッチング、トレイト(trait)による柔軟な継承などが特徴です。Apache Spark、Apache Kafka、Akkaなどの大規模分散システムやビッグデータ処理フレームワークで広く採用されており、特に並行処理やリアクティブプログラミングの分野で強力な機能を提供します。Javaとの高い互換性により既存のJavaライブラリを活用でき、関数型プログラミングの恩恵を受けながら実用的なアプリケーション開発が可能です。
書き方の例
Hello World
// 基本的な出力
object HelloWorld extends App {
println("Hello, World!")
// 変数を使った出力
val message = "こんにちは、Scala!"
println(message)
// 文字列補間
val name = "太郎"
val age = 25
println(s"私の名前は${name}で、${age}歳です。")
// フォーマット文字列
printf("数値: %d, 浮動小数点: %.2f%n", 42, 3.14159)
}
// main関数を使った書き方
object HelloWorldMain {
def main(args: Array[String]): Unit = {
println("Hello, World!")
}
}
変数とデータ型
object VariablesAndTypes extends App {
// 不変変数(val)
val immutableInt: Int = 42
val immutableString: String = "文字列"
val immutableDouble: Double = 3.14159
val immutableBoolean: Boolean = true
// 可変変数(var)
var mutableInt: Int = 100
var mutableString: String = "変更可能"
// 型推論
val inferredInt = 42 // Int型と推論される
val inferredString = "推論" // String型と推論される
// コレクション型
val list: List[Int] = List(1, 2, 3, 4, 5)
val array: Array[String] = Array("a", "b", "c")
val map: Map[String, Int] = Map("one" -> 1, "two" -> 2, "three" -> 3)
val set: Set[Int] = Set(1, 2, 3, 2, 1) // 重複は除去される
// タプル
val tuple2: (String, Int) = ("太郎", 25)
val tuple3: (String, Int, Boolean) = ("花子", 30, true)
// Option型(nullの代替)
val someValue: Option[String] = Some("値あり")
val noneValue: Option[String] = None
// 出力例
println(s"整数: $immutableInt")
println(s"リストの要素数: ${list.length}")
println(s"マップの値: ${map("one")}")
println(s"タプルの最初の要素: ${tuple2._1}")
// 変数の変更
mutableInt = 200
println(s"変更後の値: $mutableInt")
}
条件分岐
object ConditionalStatements extends App {
val score = 85
// 基本的なif式
val grade = if (score >= 90) {
"A"
} else if (score >= 80) {
"B"
} else if (score >= 70) {
"C"
} else {
"D"
}
println(s"得点: $score, 評価: $grade")
// 複数条件の組み合わせ
val age = 20
val hasLicense = true
val canDrive = if (age >= 18 && hasLicense) {
"運転できます"
} else if (age >= 18) {
"免許を取得してください"
} else {
"18歳になってから免許を取得してください"
}
println(canDrive)
// パターンマッチング
val day = "水曜日"
val dayType = day match {
case "月曜日" | "火曜日" | "水曜日" | "木曜日" | "金曜日" => "平日"
case "土曜日" | "日曜日" => "週末"
case _ => "不明"
}
println(s"$day は $dayType です")
// 値によるパターンマッチング
val number = 42
val numberDescription = number match {
case 0 => "ゼロ"
case n if n > 0 && n <= 10 => "1から10の数"
case n if n > 10 && n <= 100 => "11から100の数"
case _ => "その他の数"
}
println(s"$number は $numberDescription です")
// Option型のパターンマッチング
val optionalValue: Option[String] = Some("値")
val result = optionalValue match {
case Some(value) => s"値あり: $value"
case None => "値なし"
}
println(result)
}
コレクションと高階関数
object CollectionsAndHigherOrderFunctions extends App {
// リストの操作
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
// map関数(変換)
val squares = numbers.map(x => x * x)
val squaresShort = numbers.map(_ * _) // より簡潔な書き方
println(s"2乗: $squares")
// filter関数(フィルタリング)
val evenNumbers = numbers.filter(x => x % 2 == 0)
val evenNumbersShort = numbers.filter(_ % 2 == 0)
println(s"偶数: $evenNumbers")
// reduce関数(畳み込み)
val sum = numbers.reduce((a, b) => a + b)
val sumShort = numbers.reduce(_ + _)
println(s"合計: $sum")
// fold関数(初期値付き畳み込み)
val product = numbers.fold(1)((a, b) => a * b)
println(s"積: $product")
// 関数の組み合わせ
val result = numbers
.filter(_ % 2 == 0) // 偶数のみ
.map(_ * 3) // 3倍
.filter(_ > 10) // 10より大きい
.sum // 合計
println(s"チェーン処理の結果: $result")
// for式(comprehension)
val combinations = for {
x <- List(1, 2, 3)
y <- List('a', 'b', 'c')
if x > 1 // ガード条件
} yield (x, y)
println(s"組み合わせ: $combinations")
// ネストしたfor式
val matrix = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
val flattenedDoubled = for {
row <- matrix
element <- row
if element % 2 == 0
} yield element * 2
println(s"平坦化と2倍: $flattenedDoubled")
// Map操作
val grades = Map("太郎" -> 85, "花子" -> 92, "次郎" -> 78)
// マップの変換
val adjustedGrades = grades.map { case (name, grade) =>
name -> (grade + 5)
}
println(s"調整後の成績: $adjustedGrades")
// マップのフィルタリング
val highGrades = grades.filter { case (_, grade) => grade >= 80 }
println(s"高得点者: $highGrades")
}
関数の定義と高階関数
object FunctionsAndHigherOrder extends App {
// 基本的な関数定義
def greet(name: String): String = {
s"こんにちは、${name}さん!"
}
// 単一式の関数(中括弧省略)
def square(x: Int): Int = x * x
// デフォルト引数
def calculateArea(width: Double, height: Double = 10.0): Double = {
width * height
}
// 可変長引数
def sum(numbers: Int*): Int = {
numbers.sum
}
// 高階関数(関数を引数に取る)
def applyOperation(x: Int, y: Int, operation: (Int, Int) => Int): Int = {
operation(x, y)
}
// 関数を返す関数
def createMultiplier(factor: Int): Int => Int = {
(x: Int) => x * factor
}
// 無名関数(lambda関数)
val add = (x: Int, y: Int) => x + y
val multiply = (x: Int, y: Int) => x * y
// 部分適用
def addThreeNumbers(x: Int, y: Int, z: Int): Int = x + y + z
val addFive = addThreeNumbers(5, _, _) // 第1引数を5に固定
// カリー化
def curriedAdd(x: Int)(y: Int)(z: Int): Int = x + y + z
val addTen = curriedAdd(10) _
val addTenAndFive = addTen(5)
// 関数の使用例
println(greet("山田"))
println(s"面積: ${calculateArea(5.0)}")
println(s"合計: ${sum(1, 2, 3, 4, 5)}")
println(s"演算結果: ${applyOperation(10, 20, add)}")
println(s"演算結果: ${applyOperation(10, 20, multiply)}")
val doubler = createMultiplier(2)
println(s"2倍: ${doubler(15)}")
println(s"部分適用: ${addFive(10, 15)}")
println(s"カリー化: ${addTenAndFive(7)}")
// 再帰関数
def factorial(n: Int): Int = {
if (n <= 1) 1
else n * factorial(n - 1)
}
// 末尾再帰最適化
@scala.annotation.tailrec
def factorialTailRec(n: Int, acc: Int = 1): Int = {
if (n <= 1) acc
else factorialTailRec(n - 1, n * acc)
}
println(s"階乗: ${factorial(5)}")
println(s"末尾再帰階乗: ${factorialTailRec(5)}")
}
クラスとオブジェクト指向
// 基本的なクラス定義
class Person(val name: String, var age: Int) {
// プライベートフィールド
private var _id: String = ""
// セカンダリコンストラクタ
def this(name: String, age: Int, id: String) = {
this(name, age)
this._id = id
}
// メソッド
def greet(): String = s"こんにちは、私は$nameです。"
def haveBirthday(): Unit = {
age += 1
println(s"$nameは${age}歳になりました。")
}
// getter/setter
def id: String = _id
def id_=(newId: String): Unit = {
if (newId.nonEmpty) _id = newId
}
override def toString: String = s"Person($name, $age)"
}
// 継承
abstract class Animal(val name: String) {
def makeSound(): String // 抽象メソッド
def introduce(): String = s"私は$nameという動物です。"
}
class Dog(name: String, val breed: String) extends Animal(name) {
override def makeSound(): String = "ワンワン"
def fetch(): String = s"$nameがボールを取ってきました。"
}
class Cat(name: String, val color: String) extends Animal(name) {
override def makeSound(): String = "ニャーニャー"
def purr(): String = s"$nameがゴロゴロと鳴いています。"
}
// トレイト(trait)
trait Flyable {
def fly(): String = "空を飛んでいます"
def altitude: Double // 抽象フィールド
}
trait Swimmable {
def swim(): String = "水中を泳いでいます"
}
// 複数のトレイトをミックスイン
class Duck(name: String) extends Animal(name) with Flyable with Swimmable {
override def makeSound(): String = "クワックワッ"
override val altitude: Double = 100.0
override def fly(): String = s"$nameが${altitude}mの高さを飛んでいます"
override def swim(): String = s"$nameが池で泳いでいます"
}
// ケースクラス
case class Point(x: Double, y: Double) {
def distance(other: Point): Double = {
math.sqrt(math.pow(x - other.x, 2) + math.pow(y - other.y, 2))
}
}
// シングルトンオブジェクト
object MathUtils {
val PI: Double = 3.14159
def circleArea(radius: Double): Double = PI * radius * radius
def max(a: Int, b: Int): Int = if (a > b) a else b
}
// 使用例
object ObjectOrientedExample extends App {
// クラスのインスタンス化
val person1 = new Person("田中太郎", 30)
val person2 = new Person("山田花子", 25, "ID001")
println(person1.greet())
person1.haveBirthday()
// 継承とポリモーフィズム
val animals: List[Animal] = List(
new Dog("ポチ", "柴犬"),
new Cat("ミケ", "三毛"),
new Duck("ドナルド")
)
animals.foreach { animal =>
println(s"${animal.introduce()} 鳴き声: ${animal.makeSound()}")
}
// トレイトの使用
val duck = new Duck("ダック")
println(duck.fly())
println(duck.swim())
// ケースクラスの使用
val point1 = Point(0, 0)
val point2 = Point(3, 4)
println(s"距離: ${point1.distance(point2)}")
// パターンマッチングでケースクラスを分解
point1 match {
case Point(0, 0) => println("原点です")
case Point(x, 0) => println(s"x軸上の点($x, 0)")
case Point(0, y) => println(s"y軸上の点(0, $y)")
case Point(x, y) => println(s"一般的な点($x, $y)")
}
// シングルトンオブジェクトの使用
println(s"円の面積: ${MathUtils.circleArea(5.0)}")
println(s"最大値: ${MathUtils.max(10, 20)}")
}
並行処理とFuture
import scala.concurrent.{Future, ExecutionContext}
import scala.util.{Success, Failure}
import scala.concurrent.duration._
object ConcurrencyExample extends App {
// 暗黙的なExecutionContext
implicit val ec: ExecutionContext = ExecutionContext.global
// 基本的なFuture
val future1: Future[Int] = Future {
Thread.sleep(1000)
42
}
val future2: Future[String] = Future {
Thread.sleep(500)
"Hello, Future!"
}
// Futureの結果を変換
val doubledFuture: Future[Int] = future1.map(_ * 2)
// Futureの組み合わせ
val combinedFuture: Future[String] = for {
number <- future1
text <- future2
} yield s"$text 数値: $number"
// Futureの完了を処理
future1.onComplete {
case Success(value) => println(s"成功: $value")
case Failure(exception) => println(s"失敗: ${exception.getMessage}")
}
// 複数のFutureを並列実行
val futures = List(
Future { Thread.sleep(100); 1 },
Future { Thread.sleep(200); 2 },
Future { Thread.sleep(150); 3 }
)
val allResults: Future[List[Int]] = Future.sequence(futures)
// エラーハンドリング
val riskyFuture: Future[Int] = Future {
if (scala.util.Random.nextBoolean()) {
throw new RuntimeException("エラーが発生しました")
}
100
}
val safeFuture: Future[Int] = riskyFuture.recover {
case _: RuntimeException => 0
}
// Futureの結果を待機(実際のアプリケーションでは推奨されません)
import scala.concurrent.Await
try {
val result = Await.result(combinedFuture, 2.seconds)
println(s"結果: $result")
} catch {
case _: java.util.concurrent.TimeoutException =>
println("タイムアウトしました")
}
// アプリケーション終了を待機
Thread.sleep(2000)
}
特徴的な機能
パターンマッチングの高度な使用
object AdvancedPatternMatching extends App {
// sealed クラスとパターンマッチング
sealed trait Expression
case class Number(value: Double) extends Expression
case class Variable(name: String) extends Expression
case class Add(left: Expression, right: Expression) extends Expression
case class Multiply(left: Expression, right: Expression) extends Expression
def evaluate(expr: Expression, variables: Map[String, Double]): Double = expr match {
case Number(value) => value
case Variable(name) => variables.getOrElse(name, 0.0)
case Add(left, right) => evaluate(left, variables) + evaluate(right, variables)
case Multiply(left, right) => evaluate(left, variables) * evaluate(right, variables)
}
// リストのパターンマッチング
def listPattern(list: List[Int]): String = list match {
case Nil => "空のリスト"
case head :: Nil => s"要素1つ: $head"
case head :: tail => s"先頭: $head, 残り: $tail"
}
// ガード条件付きパターンマッチング
def classifyNumber(x: Int): String = x match {
case n if n < 0 => "負の数"
case 0 => "ゼロ"
case n if n > 0 && n <= 10 => "1から10"
case n if n > 10 => "10より大きい"
}
// 使用例
val expr = Add(Number(10), Multiply(Variable("x"), Number(5)))
val vars = Map("x" -> 3.0)
println(s"計算結果: ${evaluate(expr, vars)}")
println(listPattern(List(1, 2, 3)))
println(classifyNumber(5))
}
暗黙的変換とタイプクラス
object ImplicitsExample extends App {
// 暗黙的変換
implicit class StringOps(s: String) {
def isPalindrome: Boolean = s == s.reverse
def toSnakeCase: String = s.replaceAll("([A-Z])", "_$1").toLowerCase.dropWhile(_ == '_')
}
// 暗黙的パラメータ
implicit val defaultTimeout: Int = 5000
def connectToServer(host: String)(implicit timeout: Int): String = {
s"$hostに${timeout}ms以内で接続"
}
// タイプクラスパターン
trait Printable[T] {
def format(value: T): String
}
implicit val intPrintable: Printable[Int] = new Printable[Int] {
def format(value: Int): String = s"整数: $value"
}
implicit val stringPrintable: Printable[String] = new Printable[String] {
def format(value: String): String = s"文字列: '$value'"
}
def print[T](value: T)(implicit printer: Printable[T]): Unit = {
println(printer.format(value))
}
// 使用例
println("racecar".isPalindrome)
println("CamelCase".toSnakeCase)
println(connectToServer("localhost"))
print(42)
print("Hello")
}
モナドとfor式
object MonadExample extends App {
// Option モナド
def safeDivide(x: Double, y: Double): Option[Double] = {
if (y != 0) Some(x / y) else None
}
def safeSquareRoot(x: Double): Option[Double] = {
if (x >= 0) Some(math.sqrt(x)) else None
}
// モナディック操作
val result1 = for {
division <- safeDivide(10, 2)
sqrt <- safeSquareRoot(division)
} yield sqrt
println(s"安全な計算結果: $result1")
// Either モナド(エラーハンドリング)
sealed trait ValidationError
case object InvalidAge extends ValidationError
case object InvalidEmail extends ValidationError
def validateAge(age: Int): Either[ValidationError, Int] = {
if (age >= 0 && age <= 150) Right(age) else Left(InvalidAge)
}
def validateEmail(email: String): Either[ValidationError, String] = {
if (email.contains("@")) Right(email) else Left(InvalidEmail)
}
case class User(age: Int, email: String)
def createUser(age: Int, email: String): Either[ValidationError, User] = {
for {
validAge <- validateAge(age)
validEmail <- validateEmail(email)
} yield User(validAge, validEmail)
}
println(createUser(25, "[email protected]"))
println(createUser(-5, "invalid-email"))
}
バージョン
バージョン | ステータス | 主要な特徴 | リリース年 |
---|---|---|---|
Scala 3 (Dotty) | Latest | 新しい構文、型システム改善、マクロ刷新 | 2021 |
Scala 2.13 | Current | コレクションライブラリ刷新、パフォーマンス向上 | 2019 |
Scala 2.12 | Maintained | Java 8サポート、SAM型、lambda最適化 | 2016 |
Scala 2.11 | Legacy | モジュール化、マクロ安定化 | 2014 |
参考ページ
公式ドキュメント
- Scala Documentation - 公式ドキュメント
- Scala 3 Documentation - Scala 3専用ドキュメント
- Scala Standard Library - 標準ライブラリAPI
学習リソース
- Scala Exercises - インタラクティブな学習サイト
- Functional Programming in Scala - 関数型プログラミング解説書
- Rock the JVM - Scala学習プラットフォーム
フレームワークとライブラリ
- Apache Spark - 大規模データ処理フレームワーク
- Akka - アクターモデル並行処理フレームワーク
- Play Framework - Webアプリケーションフレームワーク