kmizuの日記

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

限定継続を実現するScalaコンパイラプラグインを試してみる

現在、Scalaでファーストクラスの継続を扱うことはできないが、Scala 2.8からは、コンパイラプラグインという形で、限定継続(Delimited Continuation)を扱う機能が提供されることになっている。2.8は未だリリースされていないが、Scala 2.8のnightly buildと、コンパイラプラグインのソースをリポジトリから入手すれば、限定継続の機能を試してみることができるようだ。

というわけで、試してみた(参考:http://blog.richdougherty.com/2009/02/delimited-continuations-in-scala_24.html)

>svn co http://lampsvn.epfl.ch/svn-repos/scala/compiler-plugins/continuations/trunk continuations
>cd continuations
>set ANT_OPTS=-Xmx512m
>ant test

ここで、なんか

error: jsr166y.RecursiveAction
 does not take type parameters
   [scalac]       FJTaskWrapper.runOnCurrentThreadOrPool(new RecursiveAction[Uni
t] {

というエラーが発生。RecursiveActionは型パラメータとらねーよ、と言うことらしいのでソースをいじってnew RecursiveAction { ... }に書き換えて、もう一回ビルドしてみる。

(略)\continuations\doc\examples\continuations\Test17Webserver.scala:32: error: value jcl is not a member of package collection
   [scalac] import scala.collection.jcl.Conversions._
   [scalac]                         ^
(略)\continuations\doc\examples\continuations\Test9.scala:24: error: type mismatch;
   [scalac]  found   : xs.type (with underlying type scala.collection.mutable.It
erable[A])
   [scalac]  required: examples.continuations.Test9.Monadic[A,scala.collection.m
utable.Iterable]
   [scalac]   implicit def reflective[A](xs:Iterable[A]) = new Reflective[A,Iterable](xs)
   [scalac]
      ^
(略)\continuations\doc
\examples\continuations\Test9.scala:35: error: value reflect is not a member of List[java.lang.String]
   [scalac]       List(left.reflect[Any] -> right.reflect[Any])
   [scalac]                 ^
   [scalac] three errors found

とりあえず、試すのには支障無さそうなので、エラーが出たdoc\examples\以下のソースは全部リネームして、再度ビルド。

>ren doc\examples\continuations\Test9.Scala Test9.scala.bak
>ren doc\examples\continuations\Test17Webserver.scala Test17Webserver.scala.bak

なんかことごとくテストが失敗するが、エラーメッセージからするに、たぶんクラスパス関係の設定が足りてないのだと思われる。とりあえず、気にせずに先に進む。

  [partest] Compiling and running files
  [partest] testing: [...]\files\run\example4.scala
  [FAILED]
  [partest] testing: [...]\files\run\example5.scala
  [FAILED]
  [partest] testing: [...]\files\run\example6.scala
  [FAILED]
  [partest] testing: [...]\files\run\example2.scala
  [FAILED]
  [partest] testing: [...]\files\run\example0.scala
  [FAILED]
  [partest] testing: [...]\files\run\example3.scala
  [FAILED]
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  [partest] testing: [...]\files\run\example1.scala
  [FAILED]
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  [partest] testing: [...]\files\run\basics.scala
  [FAILED]
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  [partest] testing: [...]\files\run\example7.scala
  [FAILED]
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  [partest] testing: [...]\files\run\example8.scala
  [FAILED]
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  [partest] testing: [...]\files\run\example9.scala
  [FAILED]
  [partest] example9.scala:6: error: value Test9 is not a member of package exam
ples.continuations
  [partest]     examples.continuations.Test9.main(args)
  [partest]                            ^
  [partest] one error found
  [partest] testing: [...]\files\run\match0.scala
  [FAILED]
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  [partest] testing: [...]\files\run\match1.scala
  [FAILED]
  [partest] java.lang.NoClassDefFoundError: and
  [partest] Exception in thread "main"
  • 限定継続を使ったサンプルプログラムを書く

提供されているのは、オーソドックスなshift/resetなので、それを使って簡単なサンプルプログラムを書いてみる。以下のプログラムは、150,200,250が順に表示される事を意図している。

import scala.continuations.ControlContext._

object UseShiftReset {
  def main(args: Array[String]) {
    var k: Int => Int = null
    println(reset {
      shift{_k: (Int => Int) =>
        k = _k
        150
      } + 50
    })
    println(k(150))
    println(k(200))
  }
}
  • サンプルプログラムをコンパイルする。
    • -Xpluginでコンパイラプラグインを指定するのと、クラスパスに限定継続を使うときに必要なライブラリを指定するのを忘れずに。
>%SCALA_HOME%\bin\scalac -Xplugin:build\pack\selectivecps-plugin.jar -classpath build\build.library UseShiftReset.scala
  • サンプルプログラムを実行する。
>%SCALA_HOME%\bin\scala -Xplugin:build\pack\selectivecps-plugin.jar -classpath build\build.library;. UseShiftReset
150
200
250

無事、意図通りの結果が表示された。