It is recommended that the reader be familiar with Heron code blocks before continuing with this document
Metaprogramming, in the context of this documentation at least, refers to the ability to perform computations and to generate code at compile time. Heron metaprogramming is done entirely through the use of templates.
Heron metaprogramming is a very powerful sub-language of Heron that is functional in nature and turing-complete. Heron metaprogramming is inspired by the Boost template metaprogramming library. Like C++, Heron metaprogramming works through the type-system, but unlike C++ the type-system was designed deliberately with metaprogramming in mind.
In Heron metaprogramming is explicitly and deliberately separate from run-time computation. This allows Heron to have more expressiveness at compile-time by reducing ambiguity from run-time code. In several languages it is left up to the compiler to decide, typically in an implementation specific manner, whether a function can be evaluated at run-time or compile-time. Determining whether a function is evaluatabe at run-time or compile-time can be difficult for a programmer. In Heron all functions are evaluated only at run-time, whereas all meta-functions are evaluated at compile time.
The conundrum can be made apparrent by considering that virtually every behaviour of a Heron program is defined within libraries, which can depend entirely on conditions not known until program execution. The challenge then would be that a Heron program would have to be run while it is being compiled, making for extremely complex conditions.
A meta-value is a type, and has no storage allocation. A meta-value literal looks like a run-time literal value, e.g. 42, 3.141, "hello", etc., but can be recognized by its context. For instance, since only types can be passed to templates, within the code snippet: SizedArray<Int, 99>, we can recognized 99 as being a meta-value. Also when using define to create an alias to a value, we know that it is a meta-value being aliased.
Sometimes it is desirable to convert from a meta-value to run-time value. This is done explicitly through the "value-of" operator ( $ ). This operators takes a compile-time expression ( i.e. a meta-value ), and returns an appropriately typed run-time literal value. For instance:
types {
define pi : 3.141; // by context, we recognize this as a meta-value.
}
functions {
_main() {
print_float(pi); // error: pi is not a valid expression, it is a meta-value
print_float($pi); // fine: outputs 3.14
}
}
types {
define f_double<x> : f_mult<x, 2.0>;
define two_pi : f_double<pi>;
}
types {
define pi : 3.14;
}
types {
define print_fubar : { print_string("fubar\n") }
}
functions {
_main() {
#print_fubar;
}
}
A slightly more interesting example which exploits the locality of an expansion is as follows:
types {
define print_x : { print_int(x) }
}
functions {
_main() {
#print_x; // error: x is not defined
_int x = 3;
#print_x; // outputs 3 to the standard out
}
}
Finally a simple, but useful application of meta-function involving code-blocks is a variation of the classic Hello World program.
program MetaHelloWorld;
uses {
std.system;
}
types {
define concat_blocks<meta_code T1, meta_code T2> :
{ #T1 #T2 };
}
functions {
_main() {
#concat_blocks
<
{print_string("hello ");},
{print_string("world\n");}
>
}
}
end
program HelloWorldX;
uses {
std.system;
}
types {
define concat_blocks<meta_code T1, meta_code T2> :
{ #T1 #T2 };
define repeat_block<meta_code T, meta_int i> :
meta_if
<
i_gt<i, 0>,
concat_blocks<T, repeat_block<T, i_dec<i>>>,
{ }
>;
}
functions {
_main() {
#repeat_block<{ print_string("hello world\n"); }, 10>
}
}
end
_main() {
print_int($size_of<_int>); // outputs 4
print_int($size_of<_char>); // outputs 1
print_int($size_of<pi>); // outputs 0
}
| name | signature | description |
| i_add | meta_int, meta_int ::= meta_int | add |
| i_sub | meta_int, meta_int ::= meta_int | subtact |
| i_div | meta_int, meta_int ::= meta_int | divide |
| i_mult | meta_int, meta_int ::= meta_int | multiply |
| i_mod | meta_int, meta_int ::= meta_int | modulo |
| i_neg | meta_int ::= meta_int | negative |
| f_sin | meta_float ::= meta_float | sine |
| f_cos | meta_float ::= meta_float | cosine |
| f_tan | meta_float ::= meta_float | tangent |
| f_add | meta_float, meta_float ::= meta_float | add |
| f_sub | meta_float, meta_float ::= meta_float | subtract |
| f_div | meta_float, meta_float ::= meta_float | divide |
| f_mult | meta_float, meta_float ::= meta_float | multiply |
| f_neg | meta_float, meta_float ::= meta_float | negative |
| s_cat | meta_string, meta_string | concat strings |
| s_open | meta_string ::= meta_string | get contents of file |
| s_len | meta_string ::= meta_int | length of string |
| s_at | meta_string, meta_int ::= meta_char | character at index |
| b_and | meta_bool, meta_bool ::= meta_bool | boolean at |
| b_or | meta_bool, meta_bool ::= meta_bool | boolean or |
| b_not | meta_bool ::= meta_bool | boolean not |
| f_gt | meta_float ::= meta_float | greater than |
| f_lt | meta_float, meta_float ::= meta_bool | less than |
| i_gt | meta_int, meta_int ::= meta_bool | greater than |
| i_lt | meta_int, meta_int ::= meta_bool | less than |
| i_gteq | meta_int, meta_int ::= meta_bool | greater than or equal |
| i_lteq | meta_int, meta_int ::= meta_bool | less than or equal |
| i_eq | meta_int, meta_int ::= meta_bool | equal |
| i_neq | meta_int, meta_int ::= meta_bool | not equal |
| i_eq_zero | meta_int ::= meta_bool | equal to zero |
| i_neq_zero | meta_int, meta_int ::= meta_bool | not equal to zero |
| i_inc | meta_int ::= meta_int | plus one |
| i_dec | meta_int ::= meta_int | minus one |
It is expected that later versions of Heron will introduce meta-abstract-data-types. This is a straightfoward and logical extension of Heron meta-programming.
It is conceivable that a future version of Heron will allow metaprogramming that has the precisely the same syntax as the rest of the language.