Metaprogramming
Metaprogramming is a programming technique in which computer programs have the ability to treat other programs as their data
It means that a program can be designed to read, generate, analyse or transform other programs, and even modify itself while running
In some cases, this allows programmers to minimize the number of lines of code to express a solution, in turn reducing development time
It also allows programs greater flexibility to efficiently handle new situations without recompilation
Macros
Make your own syntax
- Happens during compile-time (before the code runs)
Example: Custom syntax
// macro (sweet.js)
syntax hi = function (ctx) {
return #`console.log('hello, world!')`;
};
// before compile/expansion
hi
// after compile/expansion
console.log("hello, world!");Example: Custom syntax for then
operator >>= left 1 = (left, right) => {
return #`${left}.then(${right})`;
};
fetch('/foo.json') >>= resp => { return resp.json() }
>>= json => { return processJson(json) }
// Instead of
fetch("/foo.json")
.then((resp) => {
return resp.json();
})
.then((json) => {
return processJson(json);
});Reflection
Example:
// Without reflection
Foo foo = new Foo();
foo.PrintHello();
// With reflection
Object foo = Activator.CreateInstance("complete.classPath.and.Foo");
MethodInfo method = foo.GetType().GetMethod("PrintHello");
method.Invoke(foo, null);// Without reflection
const foo = new Foo();
foo.hello();
// With reflection
const foo = Reflect.construct(Foo);
const hello = Reflect.get(foo, "hello");
Reflect.apply(hello, foo, []);
// With eval
eval("new Foo().hello()");# Without reflection
obj = Foo()
obj.hello()
# With reflection
obj = globals()["Foo"]()
getattr(obj, "hello")()
# With eval
eval("Foo().hello()")Introspection
We have read-only access to the structure of a program
Example: Checking keys of an object
const hero = {
health: 100,
backpack: ["carrot", "beer"],
weapon: "sword",
};
const keys = Object.keys(hero);
console.log(keys); // ['health', 'backpack', 'weapon']Self-Modification
We can change the structure
Example: Modifying function at run time
function grumpySum(a, b) {
if (a > 5) {
grumpySum = () => 0;
}
return a + b;
}
console.log(grumpySum(1, 1)); // 2
console.log(grumpySum(10, 1)); // 11
console.log(grumpySum(1, 3)); // 0
console.log(grumpySum(2, 100)); // 0Intercession
We can redefine the semantics of some language operations
Example:
const hero = {
health: 100,
};
Object.defineProperty(hero, "status", {
get: function () {
if (this.health > 50) {
return "fit like a champ";
}
return "badly hurt";
},
});
console.log(hero);
// { health: 100 }
console.log(hero.status);
// 'fit like a champ'