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

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

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

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

With obj
 .Name = "masuda"
 .Country = "Japan"
End With

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

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)、メソッドを羅列してスクリプトっぽく書くと、こんな風に書けます。

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 は単純なデータクラス

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 がビルダークラス

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 メソッド等を移動させて、こんな感じ。

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
カテゴリー: 開発 パーマリンク