非常に多くのオプションがある場合、新しいオプションを発明することは意味がないように見えますが、メタプログラミングのアイデアを実装するためのリストされた各オプションには、 Nemerle言語のアイデアと互換性のない独自の欠点があります。
まず、Nemerleはコンパイルされたプログラミング言語であるため、Ruby、Tcl、およびLISPのメカニズムは不要になりました。
LISPのマクロを使用して自動化できるタスクのクラスが大きいため、Cマクロはすべての中で最も低消費電力です。ただし、Cマクロは安全ではありません。これは副作用のある関数であり、プログラムにはわかりにくいバグが表示されます。
コード生成の利点は、言語に関連していないことですが、これにも欠点があります。プロジェクトのアセンブリは複雑です。
C ++テンプレートは優れたテクノロジーですが、出生時の負傷が多くあります。たとえば、テキストレベルで機能し、ライブラリ間の相互作用に問題があり、テンプレートの比較的単純なコードは比較的単純に見えますが、テンプレートの複雑なコードは非常に複雑に見えます。 このアプローチは、任意の複雑なコードにはシンプルなインターフェースが必要であるという暗黙の真実とは異なります。
要約すると、Nemerleのメタプログラミングのプロパティを説明できます。
- LISPのマクロシステムに似た強力なマクロシステム。
- マクロは、ソーステキストの形式ではなく、コンパイルされたアセンブリ(コンパイラーのプラグイン)の形式で配布されます。
- マクロを使用することは、ライブラリを使用することほど複雑ではありません。
それでは、Nemerleのコンテキストにおけるマクロとは何ですか?
マクロは、構文ツリーから構文ツリーへのマッピング(関数)です。 単純に、コンパイラはソーステキストから構文ツリーを構築し、マクロ属性でマークされたクラスを記述する構文ツリーの部分は、マクロ自体のアクションの下で変更され、コンパイルプロセスが続行されます。
マクロは特定のクラスとプログラム全体の両方の構文ツリーを変更できるため、Nemerleのメタプログラミングは非常に強力です。 パワーによって、私はNemerleで自動化できる多くのタスクが他の言語よりも十分に広いことを意味します。
なんで?
メタプログラミングを使用する理由とタイミングを理解するのは難しいため、メタプログラミングを使用するタスクを分析することが最善です。 オブジェクトをシリアル化するタスクは、メタプログラミングタスクです。 たとえば、オブジェクトツリーを定義するクラスがあるとします
class A
{
public Foo : int { get ; set ; }
public Bar : string { get ; set ; }
}
class B
{
public Baz : A { get ; set ; }
public Pub : byte { get ; set ; }
}
* This source code was highlighted with Source Code Highlighter .
次に、シリアル化コードを追加します。
interface ISerializable
{
Serialize(stream : StreamWriter) : void ;
}
class A : ISerializable
{
public Foo : int { get ; set ; }
public Bar : string { get ; set ; }
public Serialize(stream : StreamWriter) : void
{
stream.WriteLine( "<A>" );
stream.WriteLine($ "<Foo><int>$Foo</int></Foo>" );
stream.WriteLine($ "<Bar><string>$Bar</string></Bar>" );
stream.WriteLine( "</A>" );
}
}
class B : ISerializable
{
public Baz : A { get ; set ; }
public Pub : byte { get ; set ; }
public Serialize(stream : StreamWriter) : void
{
stream.WriteLine( "<B>" );
stream.WriteLine( "<Baz>" );
when(Baz!= null ) Baz.Serialize(stream);
stream.WriteLine( "</Baz>" );
stream.WriteLine($ "<Pub><byte>$Pub</byte></Pub>" );
stream.WriteLine( "</B>" );
}
}
* This source code was highlighted with Source Code Highlighter .
明らかに、最初の例のコードを2番目の例のコードに変換する正式なアルゴリズムがあります。このアルゴリズムがNemerle言語で記述されている場合、マクロを取得します。 次に、シリアル化をサポートするコードを次のように書き換えることができます。
[MakeSerializable]
class A
{
public Foo : int { get ; set ; }
public Bar : string { get ; set ; }
}
[MakeSerializable]
class B
{
public Baz : A { get ; set ; }
public Pub : byte { get ; set ; }
}
* This source code was highlighted with Source Code Highlighter .
MakeSerializableはマクロ呼び出しです。
この例に基づいて、メタプログラミングを使用するときが来たという兆候を導き出すのは簡単です。
メインアルゴリズムの動作を保証するコードに対して繰り返し動作が実行されます。
Nemerleが主流だった場合、多くの製品が登場しなかったのは面白いことです。たとえば、AOP Postsharpツールは必要ありません。その機能はすべてマクロを介して簡単かつ単純に実装されるためです。 もう1つの有名なツールはNUnitです。最新バージョンでは、パラメータ化されたテストが追加されました。この機能は、今後のバージョンのNUnitの Nemerleのマクロを使用して簡単に実装できます 。
数値が好きな人にとって、プロジェクトでメタプログラミングを使用して数式を検索すると、コードの量が20%削減され、コードの変換が発生するという認識がコードをより構造化するため、システムの設計が改善されました。 さらに、メタプログラミングを使用すると、システムのリファクタリングが容易になりました。
結論として、メタプログラミングの使用は、補助コードを削減し、その結果、それに関連するエラーを削減することを目的としていることに再度注意したいと思います。 ここでのキーワードは「補助」という言葉なので、メタプログラミングを使用する利点は主に大規模なプロジェクトで顕著になります。