digitalmars.dip.ideas - Enums with indirections
- Quirin Schroll (97/97) May 13 ### What enums ought to be
- Dukc (4/27) May 13 Yes, I think this should behave as you propose.
- Quirin Schroll (3/5) May 18 You’re right, this might be what it ought to be already.
The general idea behind D’s enums is that they’re “named
literals,” and a replacement for value-like C macros, i.e. those
that are used as expressions for objects. At least that’s how I
model them in my head. You have a literal and you want to have a
name to refer to it – because it conveys intent, because it’s a
complicated expression you don’t want to repeat, because it
references symbols inaccessible/private in your scope, or for
whatever reason. An enum quite different from an `immutable`
static variable, which e.g. has an address, is `immutable`, and
can be initialized at program startup, i.e. at run-time.
Enums with indirections are tricky, but my idea is―I hope―both
sane and simple:
1. The expression initializing an enum must be admissible to
initialize a `static immutable` variable at compile-time.
2. When such an enum is used, the initializing expression is
evaluated at the place of the evaluation of the enum to create
the value anew.
Condition 1 ensures that they cannot reference mutable global
state, which they arguable shouldn’t be able to. This does not
require the enum’s type to actually be immutable, though, and
thus won’t itself bar later modifications.
Condition 2 ensures that the possibly-mutable parts of the object
graph are duplicated on use. Only immutable parts can be shared
between evaluations.
The primary difference between a `pure` factory function with no
arguments and an `enum` is intent and that `enum`s can be grouped
and have `switch` support. Technically, using an `enum` is close
to invoking a `pure` factory function.
Well-known phenomenon that an enum of slice type allocates a
dynamic array when evaluated.
```d
enum int[] values = [1, 2, 3];
void main()
{
int[] xs = values, ys = values;
// as if:
int[] xs = [1, 2, 3], ys = [1, 2, 3];
assert(xs !is ys);
}
```
Simple case with two layers of indirections: Everything is newly
allocated.
```d
enum int[][] values = [[1, 2], [1, 2]];
void main()
{
auto xs = values, ys = values;
// As if:
int[] xs = [[1, 2], [1, 2]], ys = [[1, 2], [1, 2]];
assert(xs !is ys);
assert(xs[0] !is ys[0]);
assert(xs[0] !is xs[1]);
}
```
The above showcase how it’s handled by the language today and
this is also what I propose. This isn’t true anymore if we modify
the two layers of indirections a bit:
```d
enum int[][] values = () {
auto v = [1, 2];
return [v, v];
}();
void main()
{
auto xs = values, ys = values;
// Currently: As if
int[][] xs = [[1, 2], [1, 2]], ys = [[1, 2], [1, 2]];
assert(xs !is ys);
assert(xs[0] !is ys[0]);
assert(xs[0] !is xs[1]);
// Proposed: As if
int[][] xs = () { auto v = [1, 2]; return [v, v]; }();
int[][] ys = () { auto v = [1, 2]; return [v, v]; }();
assert(xs !is ys); // same
assert(xs[0] !is ys[0]); // same
assert(xs[0] is xs[1]); // inverted
}
```
With 2 or more levels, a slice can contain two pointers that
point to the same object. A literal of this object is impossible
to spell out directly (i.e. without a lambda), but it’s worth
being able to have it. The factory function for `values` can call
`dup` on one of the uses of `v` to explicitly decouple the two
entries, but with the current language semantics, the non-tree
layout isn’t possible. If we take the idea to heart that an enum
ought to be like a C macro in that the use of the enum is
replaced by the definition in a rather mindless way (except that
while in C, the replacement is syntactic and in D would be
semantic), it’s seems to me that complex object graphs should be
admissible and retained as specified in the creation expression.
It makes the language more consistent, too: Normally you need
`dup`, but in this niche case, you don’t, leading to programmers
being surprised.
May 13
On Wednesday, 13 May 2026 at 16:20:43 UTC, Quirin Schroll wrote:
This isn’t true anymore if we modify the two layers of
indirections a bit:
```d
enum int[][] values = () {
auto v = [1, 2];
return [v, v];
}();
void main()
{
auto xs = values, ys = values;
// Currently: As if
int[][] xs = [[1, 2], [1, 2]], ys = [[1, 2], [1, 2]];
assert(xs !is ys);
assert(xs[0] !is ys[0]);
assert(xs[0] !is xs[1]);
// Proposed: As if
int[][] xs = () { auto v = [1, 2]; return [v, v]; }();
int[][] ys = () { auto v = [1, 2]; return [v, v]; }();
assert(xs !is ys); // same
assert(xs[0] !is ys[0]); // same
assert(xs[0] is xs[1]); // inverted
}
```
Yes, I think this should behave as you propose.
I'm not sure this warrants a DIP though. I'm inclined to think
this is more like a bug.
May 13
On Wednesday, 13 May 2026 at 16:44:43 UTC, Dukc wrote:I'm not sure this warrants a DIP though. I'm inclined to think this is more like a bug.You’re right, this might be what it ought to be already. https://github.com/dlang/dmd/issues/23145
May 18








Quirin Schroll <qs.il.paperinik gmail.com>