DSL / Nested Function


DSL

ネスト関数

一言要約

関数呼び出しの引数として関数呼び出しを使うことで、評価順序やスコープ上の特徴を生かす。

要約

仕組み

特徴

  • 最大の特徴は、引数の評価順序。他のパターンと異なり、内側から順に評価される。
  • 内部関数の評価結果を外部(親)関数のコンテキストとして利用できる。
  • 内部関数は、外部の関数が使いやすい値を直接返せる。
    • メソッドチェーン(イクスプレッションビルダ)と比べてみるとわかる。
  • 停止問題と無縁で、コンテキスト変数も不要

必須要素の扱い

  • 文法上必須の要素(parent ::= first second)はparent(first, second)の形で直接的に扱える。
  • メソッドチェーンと真逆なのが面白い。

引数による読みづらさの改善

  • 関数呼び出し時の引数には名前がないので、読みやすさが犠牲になる。
  • ラッピング関数を導入することで改善できる。
  • ラッピング関数を導入しても、誤った位置に関数を配置する可能性は残っている。これは、それぞれ用に独自の型(トークン)を作ることで解決できる。

省略可能要素の扱い

  • 省略可能なもの、特に(parent ::= (this | that)*)は面倒。メソッドチェーンとは真逆。以下のようなテクニックで対処する。
    • 引数のデフォルト値
    • リテラルマップ
    • あり得る引数の組み合わせごとに関数を定義
    • トークンの導入
    • コンテキスト変数の導入

複数の引数への対応

  • 以下で対応可能
    • 可変長引数
    • リテラルリスト

修飾要素の排除

  • Xxxxx.function(...のようなものをさけるには、グローバル関数かオブジェクトスコープを使う。グローバル変数をさわらないなら、グローバル関数使ってもおk。

使いどころ

  • 値の階層構造を作るときに有用
  • 省略可能な引数や可変長引数が現れたら、メソッドチェーンへの切り替えや、リテラルマップの採用を検討する。
  • やりすぎると、ひどいlispコードみたいに読みづらくなるので注意。

  • コンピュータの構成用DSL
    • Javaだとstatic importを使って修飾を外せる
    • 省略可能な引数へは、組み合わせの数だけ関数を定義して対処
    • 可変長引数は、Javaの言語機能で対処
  • トークンを導入した例(C#)
    • 内部関数はすべてトークン(型)を返す
    • 親関数はトークンを解釈し(内部データを取り出し)、目的のオブジェクトを組み上げる
  • トークンにサブタイプを導入して、IDEサポートを追加した例(Java)
    • トークンの種類ごとにサブタイプを使うことで、不適切な場所に内部関数が配置されることを防いでいる
  • 応用例:SpecificationパターンをDSLで組み立て(C#)
    • SpecificationフレームワークがSemantic Model、メソッドチェーンやネスト関数などを組み合わせてDSLを構成

ファウラーへのフィードバック

  • 担当者のつぶやきのところに少しかいた

担当者のつぶやき

  • 停止問題はない、といっているが、内部関数を汎用的に作ったら同じ問題が発生する気がするんだけど気のせい?すぐ上で言っているように、値を返す(ビルダを返すわけじゃない)から、途中で止まっても使える値がとれるからいいのだろうか。そんな気もするが、それなら「processor関数が最後に評価されるので」というのはあまりわかりやすくない。

みんなの突っ込み

  • 駐車場なかったんだろうか? -- kakuda? 2009-12-12 (土) 04:34:46