uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

Writableは何のため?

Groovyにはgroovy.lang.Writableというインターフェースがあります。定義は以下の感じ。

package groovy.lang;
interface Writable {
  Writer writeTo(Writer out);
}

また、クロージャにはasWritable()というメソッドがあり、「クロージャ引数である出力ストリームoutに対して出力処理を行うクロージャ」を、Writableインターフェースを実装したクラスのインスタンスに変換します。つまり、

x = { out -> /* outに対する出力処理 */ }.asWritable()

は、

class Hoge implements Writable {

  Writer writeTo(Writer out) {
    /* outに対する出力処理 */
  }
}

x = new Hoge()

とだいたい等価です。
なおこれは、例の「クロージャをインターフェースに変換する方法」を使えば、

x = { out -> /* outに対する出力処理 */ } as Writable

と同じでもあります(試してませんが)。

んで、本題ですが、Writableとは何か、ですが、これはおそらく、ストリーミング的に処理をするための道具です。たとえば、テンプレートエンジン*1を展開するときに

 HogeTemplate t = new HogeTemplate(..)
 String result = t.expand(binding)
 println result

とやると、展開結果後のテキストの文字列領域が必要になります。この領域は使ったらすぐ解放するものであり、展開結果が巨大になると性能に対する負荷もバカになりません。

しかし、

 HogeTemplate t = new HogeTemplate(..)
 Writable tw = t.asWritable()
 tw.writeTo(System.out)

のようにテンプレート展開エンジンをWritableとして実装できれば、ストリーミング処理になり、ある程度のバッファがあれば、メモリ使用量が限定されます(展開後のテキストの量に応じたメモリを使わなくても良い)。

良くできた仕組みと思います。

ただ、ストリーミング処理自体は普通にやるべきことで、Groovyの発明ではなく、世のテンプレートエンジンと呼ばれるもので実用的なもの(JSP,Velocity,ERB..)はあまねくこの種の処理をやっているでしょうね。

*1:以下は適当な例としてのテンプレートエンジンで、メソッドとかは実際のGroovyのSimpleTemplateとかと異なります。ただ、実際の処理方法としては、SimpleTemplateEngineやGStringTemplateEngineも原理的にはこんなことをやっているようです。