In Glint, templates look a lot like functions, BUT THEY ARE NOT FUNCTIONS. If you only remember one thing from this file, let it be that warning. Please heed; so on–so forth.
The simplest valid template is the identity template. It effectively just applies a type constraint at compile time to whatever argument you pass to it.
template(x : int) x;
;; ends up as
;; TemplateExpr
;; |-- Body: NameRefExpr
;; `-- Parameters...: VarDecl
Now, the above source code represents a Template Expression. It is an expression that may be invoked to generate an actual, “concrete” expression. That is, the above template does not end up in the final code of the program.
You may also assign a template expression to a name.
my_template :: template(x : int) x;
my_template 69;
;; ends up as
;; IntegerLiteral
It should be known that, since template invocations are expanded at compile-time, the type of a template parameter may be a type itself. That is, a template argument may be a type expression.
foo :: template(x : type) x;
bar : foo int; ;; expands to 'bar : int'
This is useful for Glint module authors to export templates instead of concrete types.
export vector :: template(elem_t : type)
struct { data:elem_t.ptr; size:uint; capacity:uint; };
foo : vector int;
In order to actually use a template to “stamp out” code, we must invoke it (by calling it). The arguments we pass to it will be checked against the parameters declared within it.
(template(x : int) x) 69;
;; ends up as
;; IntegerLiteral
As you can see, invoking a template expression removes the template expression from the program, and leaves just the body of the template with template parameters replaced with their argument counterparts.