Template and Type Literals
Atila Neves
atila.neves at gmail.com
Thu May 30 18:42:02 UTC 2024
On Tuesday, 21 May 2024 at 17:38:41 UTC, Quirin Schroll wrote:
> 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.
I've wanted "metafunction lambdas" for a while now, as well as
fixing the historical mistake we made with eponymous templates.
What mistake, I hear you ask? Well, here's something we fixed wrt
C++:
```
struct Foo {
Foo() { /* ... */ }
~Foo() { /* ... */ }
};
```
Java inherited the same mistake, namely that we have to repeat
the type's name to declare constructor(s) and the destructor.
This makes refactoring harder, which is a Bad Thing(tm). In D we
use `this` and Bob's your proverbial uncle.
Unfortunately, we then went and did something nearly exactly the
same with templates that's caused me hours of debugging when I
decide to rename the thing and don't immediately catch it because
of reflection or `__traits(compiles)`.
Anyway, all to say that I've long wanted to write template
lambdas and use `this` instead of a template's name.
More information about the dip.ideas
mailing list