ScalaでUsingすることとローンパターン

C#にはusingというとっても素晴らしい構文があります。

[C#]Usingを使え

こいつは明示的に開放することが必要なリソースのスコープを厳格に定義し、それを抜ければ何が何でもリソースがクローズするということを簡単に表現できる構文になっています。

[csharp]
using (FileStream fs = new FileStream("./aho.txt", FileMode.Open, FileAccess.Read))
using (StreamReader sr= new StreamReader(fs, Encoding.UTF8))
{
// なんかの処理
}
[/csharp]

上記のように書くと、fsとsrはスコープ(続く {  } の中)を抜けた段階でクローズされます。処理の途中で例外がスローされても正しくクローズされます。

C#知らない人は「なにを持ってリソース解放処理と定義するのだ?」と疑問に思うかもしれませんが、using節を利用してリソース解放できるのはIDisposableインタフェースを持っているクラスのインスタンスだけと決まっています。IDisposableはDisposeという単一のメソッドを持つインタフェースであり、これを実装するクラスではリソース解放処理をDisposeに書く、という決まりになっています。この仕組みをみんな守っている限り、正しく動作します。

ちなみに、私の記憶が確かならばこれはシンタックスシュガーで、逆コンパイルすると普通にtry節に変換されていたと思います。

構文でありながらIDisposableというひとつのクラスに依存しているのが気持ち悪いのですが、まあそれを言ったらasync / awaitもTask<T>に依存してますし、とりわけIDisposableなんかは今後実装を変えなければならないような要素も無いんで気持ち悪いだけで別に困らないのかな?とも思います。ただ、誰かがasync / awaitはもっと汎用的なモナドとして実装してくれたらよかったのに、と言っていましたが、Scalaを触ってみてからは私もそう思うようになりました。

まー、.NETはF#もあるんでより関数型プログラミングをしたい人はF#使えって話なのかもしれないですけど。でも私はそこまで関数型プログラミングできるわけでもないんですよね…。

次にScala。ScalaにはUsing節というのは無いですが、似たような事はできます。

詳しく解説してくれているのが、@xuweiさんの下記ブログ記事。

scala勉強会第3回のことなど

[scala]
object Utility{

/** C#のusing文のパクリ
* @tparam A closeという名前を持っているものならなんでも
* @tparam B 2番目の引数の関数オブジェクトの戻り値の型(これもなんでもいい)
* @param resource 最後にcloseを必ず呼ばないといけないオブジェクト
* @param func 1つめの引数のオブジェクトを使用して行う処理
*/
def using[A <: {def close()},B](resource:A)(func:A => B):Unit = {
try{
func(resource) //処理実行
}catch{
case e:Exception => e.printStackTrace //エラーキャッチしたら、とりあえずログはいておく。(本当はこの部分の処理とか変える必要あるはず)
}finally{
if(resource != null) resource.close() //エラーが起きても起きなくても、必ず最後にcloseを呼ぶ(close呼ぶことによって、さらに例外が発生するようなものの場合、さらに何らかの処理が必要ですが・・・(´・ω・`) )
}
}
}
[/scala]

このような関数を定義しておけばC#のusing節とほぼ同じように使うことができます。(func:A => B)の部分は中括弧で書くことができるので、

[scala]
using(new SomeResource){ s =>
s.someProcess
}
[/scala]

みたいな書き方ができます。つまり、制御構造を作れるともいえます。

この方法はコップ本にも記されていて、ローンパターンと呼ばれるらしいです(リソースを借りて処理をするので)。

ただ、上記例だとC#のように複数のインスタンスに対して書くことは出来ないんですよね。タプルやコレクションで渡したりすればできますが、どうも綺麗に書けそうにない(私が思いつかないだけで何か良いアイディアがあるとは思いますが)。で、何とかならんかな…とずっと思ってきたのですが、実際問題、複数のインスタンスを書けなくても良いような気がしてきました。

Scalaは制御構文を簡単に作れるので、何か繰り返し同じようなリソースの組を確保・開放するという処理があるならばそれ専用のそれぞれの制御構文を作ってしまえば良いんですね。Playを使ってると良く出てきますよね。withTransactionとかwithConnectionとか。そう考えるとむしろC#より柔軟かなあ?という風に思えてきました。