Scalaのシングルトン型(~.type)について

Scalaのソースコードを読んでいたりするとたまに~.typeというものが出てきてこれはなんだろうと思ったが、検索方法もよくわからんし何なのかも不明だったので調べてまとめ。

それでこいつは何なのか

名前はシングルトン型(singleton type)というらしい。
コップ本を久々に見て知った。
説明的に言うとあるオブジェクトに特異的な型。
うーんわからん。
要するにある値そのものを指す型のことらしい。

@ case class A(value: Int)
defined class A

@ val a = A(1)
a: A = A(1)

@ var b: A = null
b: A = null

@ b = a

@ b
res4: A = A(1) // 当然A型を代入できる

@ var c: a.type = null
c: A = null // 一応型はAと表示される

@ val d = A(2)
d: A = A(2)

@ c = a // aは代入できる

@ c = d // dはA型なのに代入できない
cmd8.sc:1: type mismatch;
 found   : ammonite.$sess.cmd6.d.type (with underlying type ammonite.$sess.cmd0.A)
 required: ammonite.$sess.cmd1.a.type
val res8 = c = d
               ^
Compilation Failed

上の例で行くとdはA型なのにcに代入できないということだ。
cにはaしか代入できない。
これを可能にしているのがシングルトン型。

それでなんの役に立つのか

いい例はコップ本の29.6にのってるので見ればいいと思う。
長いので省略。

簡単な例を出すとscala.collection.genericGrowableとかがこれを使用している。
Growableは何者かと言うと+=などのmutableメソッドのインターフェース。
+=のシグネチャは以下。

  def +=(elem: A): this.type

つまりどういうことかと言うと、Glowableは自身の継承先の具体クラスを知らずにその型を返すことができるということだ。
Glowableを継承するArrayBufferはこのメソッドを以下のように実装している。

  def +=(elem: A): this.type = {
    ensureSize(size0 + 1)
    array(size0) = elem.asInstanceOf[AnyRef]
    size0 += 1
    this
  }

これで当然このメソッドはArrayBufferを返すようになるのだ。
this.typeがないとジェネリクスに自身を渡す実装をしたりscala 2.12までのcollection用のBuilderを使ったりしないといけない。
まあ多少コード分量が減る。

Scala summoner pattern 型レベル多項式の微分 ScalaでAuxパターンをするときにはimplicitの順番に気をつけよう
View Comments
There are currently no comments.