Continuation-passing style で複数リソースの try-with-resource 構文を入れ子なしで書く
前回の話の続き。
末尾の参考文献によれば、CPS: Continuation-passing style(継続渡しスタイル) によって入れ子なしの記述を実現する方法もあるとのことだったので、理解を深めるために書き直しながら試してみた。 Scala の継続渡しスタイル関連の関数は、scala.coutinuationsをインポートすれば使える。 ただし、2.11では標準ライブラリから切り離されてしまったので、sbtに依存関係を書く必要がある。
このスタイルでは、ある関数に、通常のように別の関数の結果値を渡す代わりに、「継続」(残りの処理)を関数として渡す。 例えば、以下の様な感じ。
これは、reset
とshift
という関数で表現される。
reset
は、「継続」として利用する処理のスコープで、shift
は継続を渡したい処理。
shift
に渡される関数の引数cont
は、reset
の内側の処理のうち、当該shift
を評価し終わった後の残りの処理。
つまり、3行目の val num =
の代入処理と、7〜8行目の処理がcont
関数として渡される。
なので、上のプログラムは、次の順序で処理される。
- 2行目:
"A"
を出力 - 3行目:
shift
の中を評価し始める - 4行目:
"B"
を出力 - 5行目:内側の
cont(1)
を呼び出す。 1
を shiftの評価値として、「継続」(cont
関数)を実行- 3行目:
num = 1
- 7行目:
(num,1)
を出力 - 8行目:num * 2 を評価する → これが
cont(1)
の返り値 - 5行目:外側の
cont
、cont(2)
を呼び出す。 2
を shiftの評価値として、「継続」(cont
関数)を実行- 3行目:
num = 2
- 7行目:
(num,2)
を出力 - 8行目:num * 2 を評価する → これが
reset
の返り値 - 1行目:
n = 4
- 10行目:
(n,4)
を出力
これを踏まえて、複数リソースのtry-with-resource 構文を考えると、次のように書ける。
14〜26行目は、省略せずに書くと
のようになる。処理の順序は以下のとおり。
- 一つ目の
shift
を評価し始める。 - 一つ目の
using
を評価。tryの中で"op"
を出力。 - 第二引数
op
に代入されているcont
を評価。cont
は2行目のval w1 =
の代入と、5行目以降。 cont
の引数には、try
の中でop(resource)
としてnew PrintWriter("col1.txt")
が渡されているので、w1にnew PrintWriter("col1.txt")
が代入される。- 二つ目の
shift
を評価し始める。 - 二つ目の
using
を評価。tryの中で"op"
を出力。 - 第二引数
op
に代入されているcont
を評価。cont
は5行目のval w2 =
の代入と、8行目以降。 cont
の引数には、try
の中でop(resource)
としてnew PrintWriter("col2.txt")
が渡されているので、w2にnew PrintWriter("col2.txt")
が代入される。- 8〜16行目が処理される。この過程で
"A"
"B"
を出力。 - 二つ目の
shift
の中のusing
で使われているcont
(5行目のval w2 =
の代入と、8行目以降)が評価し終わったことになる。このusing
のfinally
節が実行される。"close"
を出力。 - 5行目以降は全て評価したことになったので、一つ目の
shift
の中のcont
も評価し終わったことになる。このusing
のfinally
節が実行される。"close"
を出力。
結果の出力は次のようになる。
下記のStackoverflowの例風に書くと、以下のようになる。
なるほど現状ではあまり簡潔な記述とは言いがたいが、一応このような方法でも実現可能だということがわかる。 ただし、そもそも標準ライブラリには入っていないし、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式を使ったスタイルがシンプルで使い勝手も良い印象。
参考文献: