$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

scalaでcase classをエイリアスする

Scalaではクラスをエイリアスしたいときはtypeというキーワードを利用する。

class Hoge() {
  def print(): Unit = { println("hoge") }
}

type Fuga = Hoge
val fuga = new Fuga()
fuga.print()

しかしこのやり方だけではcase classのエイリアスは出来ない。以下のコードを実行するとエラーが出力される。

case class Hoge() {
  def print(): Unit = { println("hoge") }
}

type Fuga = Hoge
val fuga = Fuga()
fuga.print()

// error: not found: value Fuga
// val fuga = Fuga()
//            ^
// one error found

case classの場合、valとtypeの両方を利用してエイリアスしなければならない。以下のコードでうまくいく。

case class Hoge() {
  def print(): Unit = { println("hoge") }
}

type Fuga = Hoge
val Fuga = Hoge
val fuga = Fuga()
fuga.print()

仕組み

どうしてこうなっているのかというと、以下の二点が絡んでくる。

  • object定義はtypeではエイリアス出来ず、valでエイリアス出来る
  • case classは暗黙的にコンパニオンオブジェクトを作成する

object定義はtypeではエイリアス出来ず、valでエイリアス出来る

objectは型ではないので、typeでエイリアス出来ない。(追記: xuweiさんによると、この認識は少し間違っているそうです。正しくはobjectの定義は「型と値を両方同時に定義」するとのことでした。教えて頂いてありがとうございます。)

object Hoge {
  def print(): Unit = { println("hoge") }
}

type Fuga = Hoge
Fuga.print()
// 2015-08-25-091747.scala:5: error: not found: type Hoge
// type Fuga = Hoge
//             ^
// 2015-08-25-091747.scala:6: error: not found: value Fuga
// Fuga.print()
// ^
// two errors found

そこでvalを使ってobjectをエイリアスする。

object Hoge {
  def print(): Unit = { println("hoge") }
}

val Fuga = Hoge
Fuga.print()

なぜこれだとうまくいくのか、詳細は分かっていないが、Scalaスケーラブルプログラミングによると

一般に、Scalaは定義のための名前空間を2個しか持っていない (Javaは4個ある)。

  • 値 (フ ィ ー ル ド、メ ソ ッ ド、パッケージ、シングルトンオブジェクト)
  • 型 (クラス、トレイト)

と書いてあるので、型の名前空間のaliasはtypeで、値の名前空間エイリアスはvalで出来るのではないかと思った。ただ詳しいことは分かってないので違ってるかもしれない。

case classは暗黙的にコンパニオンオブジェクトを作成する

case classは暗黙的にコンパニオンオブジェクトを作成し、そこにapplyやunapplyのメソッドを自動生成する。つまり勝手にclassとobjectを定義するということになる。

以上の二点からcase classはtypeだけではエイリアス出来ず、valも合わせてエイリアスする必要があることが分かる。

まとめ

今回はscalaでのcase classのエイリアスの方法について書いた。valでobjectがエイリアス出来る仕組みについては詳しく分かってないので、もし分かる人がいたら教えて下さい。

Scalaスケーラブルプログラミングを読んだ

Scalaの勉強をしたかったので読んだ。

この本はScalaの利用について、かなり詳しく書かれている本。Scalaの使い方にとどまらず、Scalaの内部についても触れているので、どういう仕組みで動いているかについても知ることが出来る。ただし、そのためにけっこう読むのに苦労するので、Scalaをとりあえず入門しようという用途には向かなそうだった。なんとなくわかりづらい理由はScalaの使い方と内部実装と、例に上がっているアルゴリズムの理論と、型言語の理論の説明が混ざってるからだと思う。

さらっと流し読みしたけど面白いところも多かった。例えば

  • 演算子はメソッド呼び出しにマッピングされ、全てがメソッド呼び出しとして扱われる
  • forに書かれた内容が、実際にはflatMapやmap、foreach、filterなどの関数呼び出しにマッピングされることがある
  • メソッドに渡せる型制約の柔軟な指定

など。


ただし、やはり入門でこれを読むのは挫折すると思う。入門の方法はまた今度まとめようと思うが、軽く調べた感じだと、以下の様な本をさらっと流し読みするのが良さそう。まだ読んでないけど。

またこの本だけでは、Scalaの例外処理に必須といえる、OptionやEither周りの話について全く言及されていないので、この辺りは個別に勉強する必要がある。例えば以下の様なエントリが参考になる。
yuroyoro.hatenablog.com
yuroyoro.hatenablog.com
hakobe932.hatenablog.com


まとめると、勉強になる本だが、難しいところも多かった。Perlでいうところの、初めてのPerlとプログラミングPerlの間くらいの位置付けの本だなと思う。