別にコンピュテーション式を調べていたのではなくて、「ひょっとしたらコンピュテーション式とCustomOperation属性で独自の二項演算子がエミュレートできるのでは?」と思っていて探していた訳で、結果的には「できない」なんですが、その派生としてマーチン・ファウラー著のDSL本を斜め読みしたので、その関連で。
32.4 Using Multiple Builders for the calender(Java) のところまで読みながら、局所的なマクロは業務コードを組むと良く出てくるパターンで、lex/yacc まで使わなくても(いや、yacc 使ったほうが手っ取り早いときもあるんですが)アセンブラ風のコマンドが先頭にあってパラメータが続くというパターンは解析がしやすいので、作るのも解釈するのも楽です。それもあって、手軽なものならば1回こっきりの自作コードを作っては捨ててもそんなに惜しくない。と言いますか、そのあたりが「スクリプト」感覚ですよね(最近のLLという意味ではない、昔のスクリプティングという意味で)。
C#やJavaの場合にはメソッドチェーンを使いますが(LINQの場合はクエリ式で並べるというパターンのあるかと)、VBの場合は With というキーワードがあります。
1 2 3 4 | With obj .Name = "masuda" .Country = "Japan" End With |
みたいな書き方で、obj.Name = “masuda” と羅列する代わりに With obj で囲む方法ですね。C言語の頃は、変数名が長くなると面倒くさいので、
1 2 3 4 | Person *longlonglongobjectname ; Person *o = longlonglongobjectname; strcpy( o.Name, "masuda" ); strcpy( o.Contry, "Japan" ); |
みたいに代入したりします。いや、これだとスタックを消費してしまうから、Person &o = *longlonglongobjectname; な風に参照を使ってみたりする場合もある訳ですが、この手の方法は C# でもよくやります。まあ、C# の場合は、長い変数名とかメソッド名は、インテリセンスを使って記述できるように工夫する方法もよくやりますが。
さて、さっきの With を使う方法は、VB.NET でも使えていて、メソッドにも使えるのですが、VB.NETの場合メソッド呼び出しは必ず括弧が入るので、いまいちスクリプトっぽくないんですよね。そういう点では、F# はスクリプトっぽいし、メソッドチェーンもスクリプトっぽい。見た目は大事です。
じゃあ、どうせ書くならば VBA ということで(VB6でもOK)、メソッドを羅列してスクリプトっぽく書くと、こんな風に書けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 |
.From とかで羅列していますが、最初の CalenderBuilder オブジェクトに対して With しているだけです。
これを実装するためには、2つのクラス(CalenderEvent、CalenderBuilder)が必要です。
CalenderEvent.cls は単純なデータクラス
1 2 3 4 5 6 7 8 9 | 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 |
CalenderBuilder.cls がビルダークラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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 |
関数名が To__ のようにアンダーバーをつけて伸ばしているのは、スクリプトにしたときにインデントを揃えるためとキーワード逃れです。From, To__ 関数の中身はサボっていますが。
じゃあ、コンピュテーション式風にIfとかWhileを再定義できるかという出来ないわけで、コンピュテーション式とは程遠い訳ですが、DSL には近いかなと思ったり。
DBの初期設定とかヒアドキュメントに書けるのが利点かと思います。コードに直接設定を書くので汎用性はないのですが、むしろ汎用性を無くして設定ファイルが散逸しないメリットを活かせます。
ちなみに、この場合はビルダークラス使わなくても、大抵の場合は以下で書く。
CalenderEvent メソッドのほうに From メソッド等を移動させて、こんな感じ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 |