Template and Type Literals

Quirin Schroll qs.il.paperinik at gmail.com
Tue May 21 17:38:41 UTC 2024


D has function and delegate literals (usually spelled `x => …` or 
`(x) { … }` or `function (T x) {…}`). You know them, you like 
them. I don’t have to explain to you how ugly `map`, `filter` and 
other ranges would be to use if you had to define and pass the 
function/predicate/… by name.

What D lacks is something like that for constructs that aren’t 
functions, in particular templates. The idea is that sometimes, 
one wants to pass something to a template that isn’t a value, 
e.g. a type or a template.

A type literal is something like `struct { … }`, which you could 
use as a template argument instead of a named struct. Like with 
function literals, there are probably some limitations (e.g. 
function literals can’t be recursive), however, using 
`typeof(this)` allows one to refer to the otherwise anonymous 
struct type, so from the top of my head, I can’t think of one.

More interesting are template literals. There can’t be proper 
template literals, though, but specialized ones.

What I mean by “proper template literals” is that there can’t be 
`template(T) =>` because what’s going to be after the `=>` arrow? 
Templates “return” by eponymous member, but the template has no 
name.

Example:
```d
import std.meta;

Instantiate!(alias T => const(T), int)[] xs = [ staticMap!(enum T 
=> T.sizeof, byte, short, int, long, struct{ long x, y; }) ];
// const(int)[] xs = [1, 2, 4, 8, 16];
```
For everything D has an abbreviated template syntax, there should 
be a template lambda syntax using the same keyword, i.e. `struct 
(T) { … }`, `class (T) { … }`, etc.
For aggregates, parentheses around a single type parameter are 
obligatory, but for `alias` and `enum`, they should be optional.

Passing a type or template literal is equivalent to passing the 
name of a newly defined aggregate type or template.

I intend to limit those constructs to be template arguments only. 
If you find use-cases, let me know, but I feel like allowing them 
anywhere else makes things a lot more complicated.

Grammar:
```diff
     TemplateArgument:
         Type
         AssignExpression
         Symbol
+       AggregateLiteral
+       TemplateLiteral
+
+   AggregateLiteral:
+       struct { AggregateBody }
+       union { AggregateBody }
+       class { AggregateBody }
+       interface { AggregateBody }
+
+   TemplateLiteral:
+       alias TemplateParameters => TemplateArgument
+       alias Identifier => TemplateArgument
+       enum TemplateParameters => TemplateArgument
+       enum Identifier => TemplateArgument
+       struct TemplateParameters { AggregateBody }
+       union TemplateParameters { AggregateBody }
+       class TemplateParameters { AggregateBody }
+       interface TemplateParameters { AggregateBody }
```

Normally, after `TemplateParameters`, there is an optional 
`Constraint`, but that doesn’t really make sense here.


More information about the dip.ideas mailing list