Scala日記

Scalaの備忘録。ときどき研究の話。

Continuation-passing style で複数リソースの try-with-resource 構文を入れ子なしで書く

前回の話の続き。

ym.hatenadiary.jp

末尾の参考文献によれば、CPS: Continuation-passing style(継続渡しスタイル) によって入れ子なしの記述を実現する方法もあるとのことだったので、理解を深めるために書き直しながら試してみた。 Scala の継続渡しスタイル関連の関数は、scala.coutinuationsをインポートすれば使える。 ただし、2.11では標準ライブラリから切り離されてしまったので、sbtに依存関係を書く必要がある。

このスタイルでは、ある関数に、通常のように別の関数の結果値を渡す代わりに、「継続」(残りの処理)を関数として渡す。 例えば、以下の様な感じ。

gist.github.com

これは、resetshiftという関数で表現される。 resetは、「継続」として利用する処理のスコープで、shift は継続を渡したい処理。 shiftに渡される関数の引数contは、resetの内側の処理のうち、当該shiftを評価し終わった後の残りの処理。 つまり、3行目の val num = の代入処理と、7〜8行目の処理がcont関数として渡される。 なので、上のプログラムは、次の順序で処理される。

  1. 2行目:"A"を出力
  2. 3行目:shiftの中を評価し始める
  3. 4行目:"B"を出力
  4. 5行目:内側のcont(1)を呼び出す。
  5. 1を shiftの評価値として、「継続」(cont関数)を実行
  6. 3行目: num = 1
  7. 7行目:(num,1)を出力
  8. 8行目:num * 2 を評価する → これがcont(1)の返り値
  9. 5行目:外側のcontcont(2)を呼び出す。
  10. 2を shiftの評価値として、「継続」(cont関数)を実行
  11. 3行目: num = 2
  12. 7行目:(num,2)を出力
  13. 8行目:num * 2 を評価する → これがresetの返り値
  14. 1行目:n = 4
  15. 10行目:(n,4)を出力

これを踏まえて、複数リソースのtry-with-resource 構文を考えると、次のように書ける。

gist.github.com

14〜26行目は、省略せずに書くと

gist.github.com

のようになる。処理の順序は以下のとおり。

  1. 一つ目のshiftを評価し始める。
  2. 一つ目のusingを評価。tryの中で"op"を出力。
  3. 第二引数opに代入されているcontを評価。contは2行目のval w1 =の代入と、5行目以降。
  4. contの引数には、tryの中でop(resource)としてnew PrintWriter("col1.txt")が渡されているので、w1にnew PrintWriter("col1.txt")が代入される。
  5. 二つ目のshiftを評価し始める。
  6. 二つ目のusingを評価。tryの中で"op"を出力。
  7. 第二引数opに代入されているcontを評価。contは5行目のval w2 =の代入と、8行目以降。
  8. contの引数には、tryの中でop(resource)としてnew PrintWriter("col2.txt")が渡されているので、w2にnew PrintWriter("col2.txt")が代入される。
  9. 8〜16行目が処理される。この過程で"A" "B"を出力。
  10. 二つ目のshiftの中のusingで使われているcont(5行目のval w2 =の代入と、8行目以降)が評価し終わったことになる。このusingfinally節が実行される。"close"を出力。
  11. 5行目以降は全て評価したことになったので、一つ目のshiftの中のcontも評価し終わったことになる。このusingfinally節が実行される。"close"を出力。

結果の出力は次のようになる。

gist.github.com

下記のStackoverflowの例風に書くと、以下のようになる。

gist.github.com

なるほど現状ではあまり簡潔な記述とは言いがたいが、一応このような方法でも実現可能だということがわかる。 ただし、そもそも標準ライブラリには入っていないし、Githubを見ると "The Scala Delimited Continuations Plugin and Library will continue to ship with Scala 2.11.0. However, it will no longer be included with Scala 2.12." とのことなので、新規のコードをこれを使って書くということはないだろう。 前回の記事の最後にあったライブラリのように for式を使ったスタイルがシンプルで使い勝手も良い印象。

github.com

参考文献:

  1. stackoverflow.com

  2. fits.hatenablog.com