Scala における meta programming について

これは Scala Advent Calendar 2011の3日目です

Scalaには、メタプロの機能がありません。そもそもメタプロの定義が曖昧というか、存在しないと思いますが「メタプロとは何か?」を語っても長くなるので略。
そのあたり詳しくないですが、例を挙げるとC++のtemplateでできることが、Scalaではできないなどあります。
たまに自分が欲しくなるものとしては可変長型引数ですかね。


そのあたりの話は、@cpp_akira さんのこのスライドがわかりやすいです。


で、現在Scalaにメタプロの機能がないのに、なんでこんなことを書いているかというと、2.10以降でmacroが入る話があるからです。


http://scalamacros.org/
https://github.com/scalamacros/kepler


がしかし、これはまだかなり製作途中で使える状態ではないようなので、macroの話も置いておきます(ぇ


では何の話をするかというと、既存のScalaのライブラリでコンパイル時コード生成をしているものを具体的に見ていきます。

自分が知るかぎり、以下のものがコンパイル時にコード生成しています。

  • Scalaz
    • scalaz6
    • scalaz-seven
  • scalaquery
  • specs
  • twitter4z
  • scala標準ライブラリの一部のclass
Scalaz6

Scalazは、これを書いている時点(2011-12-03)では、6.0.3がstable版です。現在 scalaz-seven の branch で次期バージョンが開発中のようです。

https://github.com/scalaz/scalaz/tree/6.0.3

Scalaz6がどこでコード生成をやっているかというと、以下のファイルです。

https://github.com/scalaz/scalaz/blob/6.0.3/project/Boilerplate.scala

Scalazはbuildにsbtを使っています。そしてsbtにはコンパイル時コード生成の機能があります。sbtのソースコード上の該当箇所だとこのあたりに定義があります。*1

https://github.com/sbt/sbt/blob/v0.10.1/main/Keys.scala#L99

余談ですが、ソースコード生成機能以外にも、resourceGeneratorなどもあるみたいですね。


実際Scalaz6で生成しているコードは、github自体には登録されてません。が、Scalazはsxrというコンパイラプラグインを使って、htmlで読みやすくなったソースコードをgh-pagesに登録してあって以下のURLから見れます。*2

http://scalaz.github.com/scalaz/scalaz-2.9.1-6.0.2/doc.sxr/

実際生成しているのは、以下のファイルです

http://scalaz.github.com/scalaz/scalaz-2.9.1-6.0.2/doc.sxr/scalaz/TupleW.scala.html

ソースコード見ると明らかに同じ事の繰り返しですね。全然DRYじゃないですねー(´・ω・`)

ScalaにはTupleがあり、要素数として1 〜 22 まであります。*3
Scalazに限らず、このようにそれぞれの要素数のTupleに対してこのようにメソッドを全部生成したい場合は、どうしてもこういう書き方をするしかありません。自動生成しているのかはべつにして、このパターンはある程度のライブラリで行われています。


Scalaz6はこの TupleW.scala しか生成していなくて、あまり説明することがないので、次にいきます。

scalaz-seven

今開発中の scalaz-seven では、scalaz6と比べてコード生成の方法がかなり変わっています。中心となるのが以下のファイルです

https://github.com/scalaz/scalaz/blob/f23ea581e19c32db02d47db503a4d75be74fbf9f/project/GenTypeClass.scala

scalaz6.0.3の Boilerplate.scala は68行だったのに対して、scalaz-seven の GenTypeClass.scala*4 361行と、5倍以上の量になっています。

そして、ファイル名にあるように TypeClass 、つまり型クラス毎に それぞれ class つくってます。一部のClassではなく、全体的にコード生成するようになるみたいですね。あとスラッシュ4つで区切りを表現して、追記できるようにしている(?)とかなんか色々工夫してるみたいです

scalaquery

先日の #rpscala で @tototoshi さんが発表してその際に、コードリーディングもしたのですが、とりあえずその際の自分のtweet(´・ω・)っ

これ書いている時点でscalaqueryは0.9.5が最新で、sbtは0.7.7が使われていた ようです。sbtの0.7系は自分もあまりくわしくないのですが、runTaskというメソッドを使って(コンパイル時 or コンパイル前の指定したタイミングで)独自にtaskを走らせているようです。具体的なコードを生成するためのコードはこんな感じです

https://github.com/szeiger/scala-query/blob/0.9.5/src/main/scala/org/scalaquery/ql/Parameters.fm#L19

fmという拡張子で、見ると一見Scalaコードのようですが、

<#list 2..22 as i>

という行があり、JSPにちょっと似たような書き方をしてます。これがテンプレートになっているみたいですね。

twitter4z

なんか書ききれなかったし、今後進化する可能性ありそうだし、あとで書く or 本人に書いてもらえばいいんじゃないでしょうか|ω・`)

scala標準ライブラリの一部のclass

すいません時間が(ry

まとめ?

こんなこと知って誰得なの?って思いますが(自分でも書いててそう思う)

えっと一言で言うと、このように一部の特殊なライブラリ側だけ負担すればいいとはいえ、このように色々な方法でコード生成が行われてしまう状況はあまりよくないので、はやくマクロ入らないかなーーーとか言いたいです。


なんかグダグダ過ぎるので、この続きはそのうちまた書きます・・・たぶん・・・ほんとすいません(´・ω・`)

*1: Scalaz6.0.3tagの時点ではsbt0.10.1を使っていたようなので、sbt0.10.1のリンク載せてます https://github.com/scalaz/scalaz/blob/6.0.3/project/build.properties

*2: なぜか6.0.3がなく、6.0.2があるので、6.0.2ですが・・・

*3: Tuple1 は、ほとんど使われませんが

*4:これ書いている2011-12-03のtreeの時点で