2012年9月4日火曜日

Scala Tips / Scala 2.10味見(5) - Future(3)

Futureのようなフレームワーク的な機能を開発する場合、関連する各種機能をどうやって疎結合にしていくのかという点が設計のポイントとなります。

future/promise機能の場合は、実行環境のスレッドメカニズムや同期実行のスケジューリングポリシーをどのように外付けにしていくのかという点がポイントとなります。以下では実行環境のスレッドメカニズムや同期実行のスケジューリングポリシーを実行コンテキストと呼ぶことにします。

その観点でScala 2.9までのscala.parallel.Futureやscala.actors.Futureを見てみると、実行コンテキストはハードコーディングされているようにみえます。

Scalaz 6 Promise

Scalaz 6のPromiseでは、実行コンテキストを表現するトレイトStrategyが導入されています。以下のStrategyが用意されており利用者が選択できるようになっています。

Executor
JavaのExecutorServiceを利用。
Naive
実行毎にスレッドを新しい起動。
Sequential
現在のスレッドを使用(並行処理しない)。
Identity
実行しない。
SwingWorker
SwingWorkerで実行。
SwingInvokeLater
SwingのDispatching threadで実行。

基本的にはExecutorを使用してJavaのExecutorServiceの枠組みで非同期処理を行います。

Naive, Sequential, Identityは性能測定や動作確認で使用すると便利そうです。SingWorker, SwingInvokeLaterはSwingのフレームワーク上で非同期実行させたい時に使用します。

このように性能測定/動作確認用途や他フレームワーク(この場合はSwing)上のスケジューリングを行いたい場合があるので、実行コンテキストをfuture機能の外付けにして、利用者に選択してもらうメカニズムは必須といえます。

2.10 Future

2.10 FutureにおいてScalaz PromiseのStrategyに相当する実行コンテキストはscala.concurrent.ExecutionContextです。

前回、前々回にScala 2.10のFutureの例として以下のようなプログラムを使いました。

def f1: Int = {
  import scala.concurrent._
  import scala.concurrent.util.Duration
  import ExecutionContext.Implicits.global

  val f = future { g(1) }
  Await.result(f, Duration.Inf)
}
def f = {
  import scala.concurrent._
  import ExecutionContext.Implicits.global
  import scala.concurrent.util.Duration

  val f1 = future { g(1) }
  val f2 = future { g(2) }
  val f3 = future { g(3) }
  go {
    val f = for {
      x <- f1
      y <- f2
      z <- f3
    } yield x + y + z
    Await.result(f, Duration.Inf)
  }
}

「scala.concurrent._」をimportしているのは当然として、「scala.concurrent.ExecutionContext.Implicits.global」をimportしています。scala.concurrent.ExecutionContext.Implicits.globalは、scala.concurrent.ExecutionContextを暗黙変数として定義したものです。

scala.concurrent.futureメソッドは、実行コンテキストであるExecutionContextを暗黙パラメタとするので、結果としてscala.concurrent.ExecutionContext.Implicits.globalとして定義されたExecutionContextがFutureに渡っていきます。

現在のところExecutionContextは、java.util.concurrent.ExecutorServiceやjava.util.concurrent.Executor版のものが用意されているだけです。scala.concurrent.ExecutionContext.Implicits.globalにも、この版のExecutionContextが設定されています。

まだまだ道具立てが充実している感じではないですが、いずれScalaz Promiseが提供しているような用途別のものが提供されてくることになるでしょう。

ノート

Scalaで使用する実現方式はおおむね「Real-World Scala: Dependency Injection (DI)」にある以下の方式に分類されます。

  • Cake Pattern
  • Structural typing
  • Implicit declarations
  • DI frameworks (e.g. Google Guice)

また、最近ではImplicit declarationsを活用した型クラスも選択肢に入ってくるでしょう。

Futureのような機能の場合は、Implicit declarationsの暗黙パラメタを使うのが定番となっているようです。Scalaz PromiseもScala Futureも暗黙パラメタを使っています。

諸元

  • Scala 2.10.0-M7
  • Scalaz 6.0.4

0 件のコメント:

コメントを投稿