Metaprogramming is the general name for a class of automation tools for a programmer. They understand code generation, preprocessor macros in C, C ++ templates, LISP macros, creating their own DSLs, as well as using dynamic languages with code generation on the fly.
Nemerle supports another metaprogramming option.
If so there are so many options, then it seems that inventing a new one does not make sense, but each of the listed options for implementing metaprogramming ideas has its own drawbacks that are not compatible with the ideas of the
Nemerle language.
Firstly, Nemerle is a compiled programming language, so the mechanisms from Ruby, Tcl and LISP fall away.
')
Macros C are the least powerful of all, since there is a large class of tasks that can be automated using macros from LISP, but not too much for macros C. In addition, macros C are not safe, since often the macro argument is calculated several times, is a function with a side effect, a subtle bug appears in the program.
The advantage of code generation is that it has no relation to the language, but from this it follows its disadvantage - the project is complicated to build.
C ++ templates are a good technology, but it has a lot of birth injuries, for example, it works at the text level, there are problems with interaction between libraries, and also relatively simple code on templates looks relatively simple, but complex code on templates looks very difficult. This approach differs from the unspoken truth that an arbitrarily complex code should have a simple interface.
Summarizing, you can describe what properties metaprogramming in Nemerle has:
- Powerful macrosystem, similar to macrosystem in LISP.
- Macros are not distributed as source code, but as compiled assemblies — plug-ins for the compiler.
- Using macros is as simple as using libraries.
So what is a macro in the context of Nemerle?A macro is a mapping (function) from a syntax tree to a syntax tree. Simplified, the source code compiler builds a syntax tree, then the parts of the syntax tree describing the classes marked with the macro attribute are modified by the action of the macro itself and the compilation process continues.
Macros can change the syntax trees of a particular class, as well as the entire program, thus, Neprole’s metaprogramming is powerful enough. By power, I mean that the set of tasks that can be automated in Nemerle is quite wider than in other languages.
What for?It is difficult to understand why and when to use metaprogramming, so it is best to analyze the tasks that use it. The task of object serialization is the task of metaprogramming. Suppose we have classes that define object trees, for example
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 .
Now add the code responsible for serialization:
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 .
Obviously, there is a formal algorithm that translates the code from the first example into the code of the second example; if this algorithm is written in the Nemerle language, then we get a macro. Then the code with serialization support can be rewritten, for example, like this:
[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 .
Where MakeSerializable is a macro call.
Based on this example, it is easy to derive a sign that it is time to use metaprogramming:
Above the code that ensures the operation of the main algorithm, repeated operations are performed.
It's funny that if Nemerle were mainstream, many products simply would not have appeared, for example, the AOP
Postsharp tool would not be necessary, since all its capabilities are easily and simply implemented through macros.
NUnit is another well-known tool. In the latest version, the parameterized tests were added, again this feature can be easily implemented through macros in Nemerle for the next version of
NUnit , and with higher type control.
For those who like numbers, the use of metaprogramming in my search project formulas reduced the amount of code by 20%, helped to improve the design of the system, since the awareness that the code will be transformed, makes writing code more structured. An additional advantage is that the use of metaprogramming has facilitated the refactoring of the system.
In conclusion, I want to once again note that the use of metaprogramming is aimed at reducing the auxiliary code and, as a result, the errors associated with it. The key word here is the word "auxiliary", so the benefits of using metaprogramming will be primarily noticeable in large projects.