F#のコンピュテーション式をVBAを使って真似してみる…というかDSLの真似

別にコンピュテーション式を調べていたのではなくて、「ひょっとしたらコンピュテーション式とCustomOperation属性で独自の二項演算子がエミュレートできるのでは?」と思っていて探していた訳で、結果的には「できない」なんですが、その派生としてマーチン・ファウラー著のDSL本を斜め読みしたので、その関連で。

32.4 Using Multiple Builders for the calender(Java) のところまで読みながら、局所的なマクロは業務コードを組むと良く出てくるパターンで、lex/yacc まで使わなくても(いや、yacc 使ったほうが手っ取り早いときもあるんですが)アセンブラ風のコマンドが先頭にあってパラメータが続くというパターンは解析がしやすいので、作るのも解釈するのも楽です。それもあって、手軽なものならば1回こっきりの自作コードを作っては捨ててもそんなに惜しくない。と言いますか、そのあたりが「スクリプト」感覚ですよね(最近のLLという意味ではない、昔のスクリプティングという意味で)。

C#やJavaの場合にはメソッドチェーンを使いますが(LINQの場合はクエリ式で並べるというパターンのあるかと)、VBの場合は With というキーワードがあります。

[code lang=”vb”]
With obj
.Name = "masuda"
.Country = "Japan"
End With
[/code]

みたいな書き方で、obj.Name = “masuda” と羅列する代わりに With obj で囲む方法ですね。C言語の頃は、変数名が長くなると面倒くさいので、

[code lang=”csharp”]
Person *longlonglongobjectname ;
Person *o = longlonglongobjectname;
strcpy( o.Name, "masuda" );
strcpy( o.Contry, "Japan" );
[/code]

みたいに代入したりします。いや、これだとスタックを消費してしまうから、Person &o = *longlonglongobjectname; な風に参照を使ってみたりする場合もある訳ですが、この手の方法は C# でもよくやります。まあ、C# の場合は、長い変数名とかメソッド名は、インテリセンスを使って記述できるように工夫する方法もよくやりますが。

さて、さっきの With を使う方法は、VB.NET でも使えていて、メソッドにも使えるのですが、VB.NETの場合メソッド呼び出しは必ず括弧が入るので、いまいちスクリプトっぽくないんですよね。そういう点では、F# はスクリプトっぽいし、メソッドチェーンもスクリプトっぽい。見た目は大事です。

じゃあ、どうせ書くならば VBA ということで(VB6でもOK)、メソッドを羅列してスクリプトっぽく書くと、こんな風に書けます。

[code lang=”vb”]
Option Explicit

Sub test()
Dim cb As New CalenderBuilder
With cb
.Add "DSL tutorial"
.On__ 2009, 11, 8
.From "09:00"
.To__ "16:00"
.At__ "Aarhus Music Hall"

.Add "Making use of Ptterns"
.On__ 2009, 10, 5
.From "14:15"
.To__ "15:41"
.At__ "Aarhus Music Hall"
End With
End Sub
[/code]

.From とかで羅列していますが、最初の CalenderBuilder オブジェクトに対して With しているだけです。
これを実装するためには、2つのクラス(CalenderEvent、CalenderBuilder)が必要です。

CalenderEvent.cls は単純なデータクラス

[code lang=”vb”]
Option Explicit

Public Name As String
Public year As Integer
Public month As Integer
Public day As Integer
Public startTime As String
Public endTime As String
Public location As String
[/code]

CalenderBuilder.cls がビルダークラス

[code lang=”vb”]
Option Explicit

Public items As New Collection
Public cur As CalenderEvent

Public Sub Add(title As String)
Set cur = New CalenderEvent
items.Add cur
cur.Name = title
End Sub

Public Sub On__(year As Integer, month As Integer, day As Integer)
With cur
.year = year
.month = month
.day = day
End With
End Sub

Public Sub From(s As String)
cur.startTime = s
End Sub

Public Sub To__(s As String)
cur.endTime = s
End Sub

Public Sub At__(s As String)
cur.location = s
End Sub
[/code]

関数名が To__ のようにアンダーバーをつけて伸ばしているのは、スクリプトにしたときにインデントを揃えるためとキーワード逃れです。From, To__ 関数の中身はサボっていますが。

じゃあ、コンピュテーション式風にIfとかWhileを再定義できるかという出来ないわけで、コンピュテーション式とは程遠い訳ですが、DSL には近いかなと思ったり。
DBの初期設定とかヒアドキュメントに書けるのが利点かと思います。コードに直接設定を書くので汎用性はないのですが、むしろ汎用性を無くして設定ファイルが散逸しないメリットを活かせます。

ちなみに、この場合はビルダークラス使わなくても、大抵の場合は以下で書く。
CalenderEvent メソッドのほうに From メソッド等を移動させて、こんな感じ。

[code lang=”vb”]
Dim obj As CalenderEvent
Dim items As New Collection

With obj
Set obj = New CalenderEvent
items.Add obj
.Name "DSL tutorial"
.On__ 2009, 11, 8
.From "09:00"
.To__ "16:00"
.At__ "Aarhus Music Hall"

Set obj = New CalenderEvent
items.Add obj
.Name "Making use of Ptterns"
.On__ 2009, 10, 5
.From "14:15"
.To__ "15:41"
.At__ "Aarhus Music Hall"
End With
[/code]

カテゴリー: 開発 パーマリンク