構文ノイズ

2008年6月9日

ドメイン特化言語 (またはあらゆるコンピュータ言語) について話す際に、よく使われるフレーズは「構文ノイズ」です。 人々は、RubyはJavaよりもノイズが少ないとか、外部DSLは内部DSLよりもノイズが少ないなどと言うかもしれません。 構文ノイズとは、実際に言いたいことの一部ではないが、言語定義を満たすために存在する余計な文字のことです。 ノイズとなる文字は、プログラムの意味を分かりにくくし、何をしているのかを理解するのに苦労させるため、良くありません。

多くの概念と同様に、構文ノイズは曖昧で主観的であるため、議論するのが難しいです。 しばらく前に、ギラッド・ブラハはJAOOでの講演で、彼が認識する構文ノイズを図示しようとしました。 ここでは、同様のアプローチを試み、現在DSLに関する書籍の導入部分で使用しているDSLのいくつかの定式化に適用してみます。(テキストを妥当なサイズに保つために、状態マシンの例のサブセットを使用しています。)

彼の講演では、ノイズと見なす文字に色を付けることでノイズを図示しました。 もちろん、この方法の問題点は、ノイズとなる文字を定義する必要があることです。 ここではそれを避け、別の区別をします。 ドメインテキストと句読点と呼ぶものを区別します。 ここで見ているDSLスクリプトは状態マシンを定義しているため、状態、イベント、コマンドについて記述しています。 特定の状態マシンの情報 (状態の名前など) を記述するものはすべてドメインテキストとして定義します。 その他はすべて句読点であり、後者を赤で強調表示します。

まずは、外部DSLのカスタム構文から始めます。

events
  doorClosed  D1CL
  drawOpened  D2OP
  lightOn     L1ON
end
   
commands
  unlockDoor D1UL
  lockPanel   PNLK
end
   
state idle
  actions {unlockDoor lockPanel}
  doorClosed => active
end
   
state active
  drawOpened => waitingForLight
  lightOn    => waitingForDraw
end

カスタム構文はノイズを最小限に抑える傾向があるため、結果として句読点は比較的少なくなります。 このテキストはまた、私たちが*いくらか*の句読点を必要としていることを明確に示しています。 イベントとコマンドはどちらも、名前とコードを指定することで定義されます。 - それらを区別するためには句読点が必要です。 したがって、句読点はノイズと同じではなく、間違った種類の句読点や句読点が多すぎるのがノイズであると言えます。 特に、句読点を絶対的な最小限にまで減らそうとするのは良い考えではないと思います。句読点が少なすぎると、DSLの理解が難しくなるからです。

次に、Rubyで記述された同じドメイン情報の内部DSLを見てみましょう。

event :doorClosed, "D1CL"  
event :drawOpened,  "D2OP"  
event :lightOn, "L1ON"  

command  :lockPanel,   "PNLK" 
command  :unlockDoor,  "D1UL" 

state :idle do 
  actions :unlockDoor, :lockPanel
  transitions :doorClosed => :active
end 

state :active do 
  transitions :drawOpened => :waitingForLight, 
              :lightOn => :waitingForDraw
end 

ここでは、より多くの句読点が見られます。 確かに、DSLで句読点を減らすための選択肢はいくつかありましたが、Ruby DSLはカスタムDSLよりも句読点が多いことに、ほとんどの人は同意すると思います。 ここでのノイズは、少なくとも私にとっては、些細なことです。シンボルを示す「:」、引数を区切る「,」、文字列を引用符で囲む「"」などです。

私のDSLに関する考え方の主なテーマの1つは、DSLはフレームワークにデータを入力する方法であるということです。 この場合、フレームワークは状態マシンを記述するものです。 DSLを使用してフレームワークにデータを入力するだけでなく、通常のプッシュボタンAPIを使用してデータを入力することもできます。 それでは、その句読点に色を付けてみましょう。

Event doorClosed = new Event("doorClosed", "D1CL"); 
Event drawOpened = new Event("drawOpened", "D2OP"); 
Event lightOn = new Event("lightOn", "L1ON"); 
 
Command lockPanelCmd = new Command("lockPanel", "PNLK"); 
Command unlockDoorCmd = new Command("unlockDoor", "D1UL"); 

State idle = new State("idle"); 
State activeState = new State("active"); 
 
StateMachine machine = new StateMachine(idle); 

idle.addTransition(doorClosed, activeState);
idle.addCommand(unlockDoorCmd);
idle.addCommand(lockPanelCmd);

activeState.addTransition(drawOpened, waitingForLightState);
activeState.addTransition(lightOn, waitingForDrawState);

ここでは、さらに多くの句読点があります。 あらゆる種類の引用符や括弧、メソッドキーワード、ローカル変数宣言などです。 後者は興味深い分類上の問題を提示します。 ローカル変数の宣言は句読点としてカウントしましたが (名前が重複するため)、後でドメインテキストとして使用されます。

Javaは流れるような書き方もできるので、本書から流れるようなバージョンを引用します。

public class BasicStateMachine extends StateMachineBuilder { 
  Events doorClosed, drawOpened, lightOn; 
  Commands lockPanel, unlockDoor; 
  States idle, active; 

  protected void defineStateMachine() { 
    doorClosed. code("D1CL"); 
    drawOpened. code("D2OP"); 
    lightOn.    code("L1ON"); 

    lockPanel.  code("PNLK"); 
    unlockDoor. code("D1UL"); 
 
    idle 
        .actions(unlockDoor, lockPanel) 
        .transition(doorClosed).to(active) 
        ; 
 
    active 
        .transition(drawOpened).to(waitingForLight) 
        .transition(lightOn).   to(waitingForDraw) 
        ; 
 } 
 

構文ノイズについて話すために2、3人が集まると、必ずXMLの話が出てきます。

<stateMachine start = "idle"> 
    <event name="doorClosed" code="D1CL"/>  
    <event name="drawOpened" code="D2OP"/> 
    <event name="lightOn" code="L1ON"/> 

    <command name="lockPanel" code="PNLK"/> 
    <command name="unlockDoor" code="D1UL"/> 

  <state name="idle"> 
    <transition event="doorClosed" target="active"/> 
    <action command="unlockDoor"/> 
    <action command="lockPanel"/> 
  </state> 

  <state name="active"> 
    <transition event="drawOpened" target="waitingForLight"/> 
    <transition event="lightOn" target="waitingForDraw"/> 
  </state>
</stateMachine> 

この特定の例から多くを読み取ることはできないと思いますが、いくつかの考察の材料を提供しています。 役に立つ句読点とノイズを厳密に区別することはできないと思いますが、ドメインテキストと句読点を区別することで、句読点に焦点を当て、どの句読点が最も役立つかを考えることができます。 また、DSLの句読点の文字数がドメインテキストの文字数よりも多い場合は、問題の兆候であると言えるかもしれません。

(ミカエル・ヤンソンはこの例のLisp版を公開しています。ミハイロ・ラレヴィッチはJavaScript版を作成しました。)