kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

Scala 3.0の新機能

注意:この記事はエイプリルフールのネタ記事です。エイプリルフールを過ぎてしばらく経つので、まぎらわしいかと思い、ここに書いておくことにします。

今年中には、再設計されたコレクションライブラリ、名前付き引数のサポート、部分継続などの数々の新機能をサポートしたScala 2.8がリリースされる見込みですが、その次の年には、早くもその次のバージョンであるScala 3.0がリリースされる予定です。Scala 3.0ではScala 2.8以上の抜本的な言語仕様の変更がおこなわれるため、その全体を短いエントリの中で紹介することは不可能ですが、ここではScala 3.0の新機能の中で特徴的なものを紹介していきます。

型システムを、structural typeベースとして全面的に再設計!

Scala 2.X系では基本的な型システムはnominal、つまり、明示的な継承関係があるかどうかでサブタイプ関係が決定されており、structural typeはあくまでオプショナルなものでしたが、Scala 3.0でstructural typeがデフォルトになります。たとえば、Scala 2.Xでは、以下のreadAllメソッドの引数にはjava.io.Readerを明示的に継承している型しか渡せませんでしたが、3.0では、Readerが持っているメソッドさえ持っていればどんな型でも渡すことができるようになります。

import java.io.Reader

def readAll(a: Reader): String = ... 

class MyReader {//Readerを継承していない
  def read(): Int = ...
  ...
}

readAll(new MyReader) // OK in Scala 3.0

def readAll2(a: Reader @Nominal): String = ...
readAll(new MyReader) // NG.  @Nominal annotation enforces nominal typing 
型推論が大幅に強化!!

2.Xでは、メソッドの仮引数の型を推論することはできないため、明示的に書いてあげる必要がありましたが、3.0では、ほとんどの場合、処理系がうまく型を推論してくれるようになりました。たとえば、以下のusingメソッドは、

def using(resource)(f) = {
  try { f(resource) } finally { resource.close() }
}

処理系によって、

def using[A <: { def close(): Unit }, B](resource: A)(f: A => B): B 

という風に推論されます。これで、MLやHaskellに比べて型推論が弱いなんて言わせません!

イレイジャベースでない新しいジェネリクス!!!

2.Xのジェネリクスは、Javaジェネリクスと同様に、コンパイル時に型パラメータを消去するイレイジャという手法によって実現されているため、C++のテンプレート等に比べて様々な制限がありました。たとえば、型パラメタをnewすると言ったことを素直には書けませんでした(Manifestを使った場合を除く)。3.0では、ジェネリクスが全面的に再設計されました。これまでの互換性を捨てることと引き換えに、これまでのさまざまな制約から解放されます。また、型パラメータとして、通常の型だけでなく、整数、浮動小数点数、真偽値など様々な値を取ることができるようになりました。3.0の新しいジェネリクスを使うと、コンパイル時に階乗を計算するというよくあるコードは次のように書くことができます。非常に直観的で簡潔な定義ですね!

//テンプレートの型パラメータはInt型と推論される
type factorial[0] = 1
type factorial[n] = n * factorial[n - 1]
拡張可能リテラル!!!!

これまでは、言語組み込みのものであったリテラルを、3.0ではユーザがある程度自由に定義できるようになります。たとえば、BigDecimalリテラルは次のように簡単に定義できます。

object DecimalLiteral {
  literal e:([0-9]+(\.[0-9]+)) suffix "b" = BigDecimal(e)
}
println(10.5b) // NG
import DecimalLiteral._
println(10.5b) // OK
爆速コンパイル!!!!!

Scalaではこれまでコンパイルがさんざん遅いと言われてきましたが、3.0ではもうそんな事は言わせまん!2.Xに比べて数十倍のコンパイル速度向上を実現しました。たとえば、以下のHelloWorldのコードを、3.0では手元のScala 3.0のnightly buildでは、わずか10ミリ秒でコンパイルできます。

object HelloWorld {
  def main(args: Array[String]) {
    println("hello, world!")
  }
}
おわりに

Scala 3.0の新機能の中でも特徴的なものをざっと紹介させていただきましたが、いかがでしたでしょうか。Scala 3.0では、型システムやメタプログラミングの機能を中心に、言語が全面的に再設計されています。そのため、従来のコードの全てがそのまま動くわけではありません。しかし、@Legacyアノテーションなど、既存のコードをそのまま動かすための仕組みも導入されているため、全面的にコードを書き直すことなく、少しずつ3.0に移行できるようになっています。また、従来Scalaの弱点とされてきた、コンパイル速度の遅さが大幅に改善されているため、新機能の部分を除いても、Scalaを実用的に使う上で移行する価値はおおいにあると思います。Scala 3.0は2011年後半にリリース予定ですが、実に楽しみですね!

もちろん、全部嘘ですが。