Scala Design Patternsを読んでいたところ、Scalaにおける値型(列挙型, Beans)の自然な表現が紹介されていたので、まとめました。
値型とは?
値型とは、メソッドを持たず、値のみを保持するオブジェクトのことです。
ここでは値型として、列挙型とBeansを紹介します。
列挙型
定義の方法
sealed abstract trait Weekday
case object Sunday extends Weekday
case object Monday extends Weekday
case object Tuesday extends Weekday
case object Wednesday extends Weekday
case object Thursday extends Weekday
case object Friday extends Weekday
case object Saturday extends Weekday
ポイント
- 列挙型の本体(
Weekday
)を、sealed
キーワードで修飾する。- こうすることで、誰かが勝手に
Weekday
を拡張して8つ目の曜日を作るすることができない。 - パターンマッチに漏れがあると、コンパイラが警告してくれる
- こうすることで、誰かが勝手に
- 本体をインスタンス化できないよう、
abstract
修飾子を施す - 具体的な値は
case object
として定義し、本体(Weekday
)を継承する-
case
キーワードを付与することで、パターンマッチが可能になる - インスタンスを区別する必要がまったくないため、
class
ではなくobject
で定義する
-
使い方
def weekdayToNum(weekday: Weekday): Int =
weekday match {
case Sunday => 0
case Monday => 1
case Tuesday => 2
case Wednesday => 3
case Thursday => 4
case Friday => 5
case Saturday => 6
}
val monday: weekDay = Monday
weekdayToNum(monday) // => 1
Beans
Beansは、プロパティとその読み書き機能のみを持つオブジェクトです(厳密な意味でのJavaBeansにはもう少し色々制約がありますが、ここではデータ型としての機能だけに着目します)。
定義の方法
sealed case class Name(firstName: String, lastName: String)
ポイント
-
sealed
を使用して勝手に拡張されないようにする -
case class
として定義し、パターンマッチを可能にする - 必要なメンバ変数をコンストラクタに定義するだけ。クラスの本体を書く必要なし。
使用法
val bob = Name("Bob", "Dylan")
bob.lastName // => Dylan
代数的データ型(ADT)
上で紹介したような値型のことを、関数型プログラミングでは代数的データ型(ADT = Algebraic Data Types)といいます。
そのうち、列挙型の表現として紹介したものをSUM ADT
、Beansの表現として紹介したものをPRODUCT ADT
と言います。
SUM ADTとPRODUCT ADTの複合型
SUM ADTとPRODUCT ADTを合体させることも可能です。
次は図形を表すADTです。
sealed abstract trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(width: Double, height: Double) extends Shape
def calcArea(shape: Shape): Double =
shape match{
case Circle(r) => r * r * 3.14
case Rectangle(w, h) => w * h
}
val c = new Circle(1)
calcArea(c) // => 3.14