kmizuの日記

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

Scala 2.10.0 M3の新機能を試してみる(1) - SIP-11 - String Interpolation and formatting

先日というかしばらく前ですが、Scala 2.10.0 Milestone 3がリリースされました。この、「マイルストーンリリース」については、以前に書いたように、RCほどの品質は無い点に注意する必要がありますが、今回のMilestone 3で2.10.0に導入予定の機能の多くがとりあえず実装されているので、遊んでみる価値はあります。

というわけで、Scala 2.10.0 M3で実装された新機能を今後数回にわたって紹介していきます。まず最初は、SIP-11 - String Interpolation and formattingです。この機能、Rubyなどの言語ユーザにとっては非常におなじみの機能だと思います。早い話が、Ruby

puts "1 + 2 = #{1 + 2}" #" 1 + 2 = 3"

と同様に、文字列リテラルの中に、実行時に評価されるScalaの式を埋め込める機能ですね。とりあえず、これと同じことをするScalaコードをScala 2.10.0 M3のREPLで書いてみましょう。

scala> println(s"1 + 2 = ${1 + 2}")
1 + 2 = 3

文字列リテラルの前にプレフィクス's'を付ける必要があるのと、式を囲む部分が${...}な以外はRubyのそれとほぼ同じですね。非常に簡単です。

ところで、何故、わざわざプレフィクスを付けなければいけない仕様になっているのでしょうか。一つは、文字列リテラルを使った既存コードを壊さないためです。通常の文字列リテラルを使った既存のコードが$などをそのままの意味で使っていたら、既存のコードの意味が変わってしまいますからね。

もう一つは、文字列リテラルに付けるプレフィクスとして、sだけでなく、任意の識別子を取る事ができるようにするためです。たとえば、プレフィクスとしてfを使ってみましょう。

scala> println(f"1 + 2 = ${1 + 2}%03d")
1 + 2 = 003

ポイントは、文字列リテラルの中で、printfで使われる書式指定文字列%...が使えていることです。fをプレフィクスとして付けることで、埋め込んだ式の書式を同時に指定する事ができるようになります。これは非常に便利な機能です。

さて、この機能、いったいどのようにして実現されているのでしょうか?上述のSIP-11へのリンクを見ると仕様は書いてあるので、詳しくはそちらを読んでいただくとして、

s"1 + 2 = ${1 + 2}"

というコードは、

scala.StringContext("1 + 2 = ").s(1 + 2)

というコードに、

f"1 + 2 = ${1 + 2}%03d")

というコードは、

scala.StringContext("1 + 2 =", "%03d").f(1 + 2)

というコードに変換されます。現在は、StringContextにはプレフィクスとして使える識別子(メソッド)はsとfしか無いようですが、自分で新しいプレフィクスを作って独自の意味を定義したりすることもできます。SIP-11では、

xml"""
<foo>${"foo" + "bar"}</foo>
"""

のようにすることで、埋め込み文字列を使用可能なXMLパーザを定義する事なども考慮に入れています。

正直、この機能の提案を始めて見たときはそれほど面白くありませんでしたが、文字列リテラルに対するプレフィクスをメソッド呼び出しとみなすことで、極めて拡張性の高い言語機能になっています。

最終的には、

StringContext(.....).id(....)

という形のメソッド呼び出しに変換するため、idに相当するメソッドの定義次第では、文字列以外のオブジェクトを返す事も考えられます。

単純にString interpolationを実装するだけなら、ここまでしなくても、Rubyのように文字列中で式を使うための追加文法さえ定義すれば良いですが、Scalaはより汎用的な機能の使用例の一つとして実装する事にしたようです。

これまでScalaの変遷を見てきた私の主観なのですが、Scalaは設計思想として、用途が限定された機能を多数追加するより、できるだけ汎用性の高い機能一つを実装して、用途に特化した部分はそれを使って実装する事を好むように感じます。そういう意味で、このSIP-11は、いかにもScalaらしい機能です。