RfC for language feature: rvalue struct
FeepingCreature
feepingcreature at gmail.com
Wed Jan 25 16:23:51 UTC 2023
Before I take on the effort of writing up and submitting a DIP,
let me solicit feedback and see if anyone can see a reason why
this idea is dumb and doesn't work.
tl;dr: `immutable struct` was a mistake: it's too weak. `rvalue
struct` is what we really want.
# aside: what are lvalues and rvalues
lvalue: an expression that has an address.
rvalue: an expression that does not.
The names come from `a = b`: an "lvalue" (left value) is a thing
that can appear on the left of the equals sign, an "rvalue"
(right value) may only appear on the right.
Example: `int a` is an lvalue. `5` is an rvalue. You can write `a
= 5` but not `5 = a`.
# rvalue struct
This is a summary of an idea from my DConf talk
https://www.youtube.com/watch?v=eGX_fxlig8I
I'm writing it up because I'm noticing that the `immutable` bugs
are neverending and the workarounds are an endless hole into
which effort and nerves are thrown to no perceivable change.
What is it? Take this struct
```
rvalue struct S
{
int a;
int[] b;
}
```
Two rules:
1. A `rvalue` struct has `immutable` fields.
`typeof(s)` is `S`, but `typeof(s.b)` is `immutable int[]`.
2. Any symbol that would be an lvalue to *a field of `S`* is
instead an rvalue.
That's it.
What is the effect of this?
This works:
```
S foo(S s) {
S value = S.init;
value = s;
*&value = s;
return s;
}
```
This does not:
```
S s;
s.a = 5;
&s.a
((ref int i) {})(s.a);
```
Note that you can overwrite `value` all you want. You can take
the address of `value`. It's not immutable. But its fields are
immutable. Isn't that a problem? No: because its fields *don't
exist.* They're rvalues. You can't address them, you can't
reference them. You can never observe a constness violation on
them, because you can only observe them as rvalues. `s.a` is
effectively an accessor.
# What's the use?
D libraries like to behave like they can declare variables and
assign values to them. (Oh, to live in such innocence!) This is
all over Phobos, Dub, etc. `immutable struct` frustrates this
belief. Because you could always take the address of a field,
which would be `immutable T*`, you could see the value changing
when you overwrite the variable - a constness violation.
`immutable` solves this by preventing you from modifying the
memory of the field while the pointer lives. This largely doesn't
work, because people don't test with `immutable struct` in the
first place. If an `rvalue struct` is used, the naive code works
as before, but the type gets the correctness benefits of
immutable: you can only construct a new value through the
constructor.
# Corporate Motivation
Some details about Funkwerk, fresh off `wc -l`: In the modern
part of our codebase, we have 648 domain structs, out of which
278 are `immutable struct`, and at least another 129 are good
candidates to make immutable. In those 113kloc, we have **no**
pointers to struct fields. None. `immutable struct` puts on the
language, on library developers and on end users, immense
difficulty and effort to protect a usecase that isn't useful.
More information about the Digitalmars-d
mailing list