Final type qualifier

Quirin Schroll qs.il.paperinik at gmail.com
Mon Jul 22 11:41:23 UTC 2024


On Saturday, 20 July 2024 at 06:55:27 UTC, harakim wrote:
> On Thursday, 18 July 2024 at 10:27:29 UTC, Quirin Schroll wrote:
>> This solves two unrelated issues:
>>
>> - `const` class objects can’t be referred to by assignable 
>> handles. It requires some trickery which probably breaks the 
>> type system. Generally, if a function returns a non-mutable 
>> class object, it’s not justified why the caller must be unable 
>> to re-assign the variable the result is put in.
>>
>> - `const` delegates are broken as they don’t respect `const` 
>> guarantees. As of today, `const` delegates are factually 
>> `final` delegates: They can’t be reassigned, but their context 
>> may change through the context pointer that’s part of the 
>> delegate. If we had `final` in a new edition, old-edition 
>> `const` delegates could become `final` delegates in the new 
>> edition and the new edition could fix `const` delegates.

Note: In your whole post, it seems you conflated `const` with 
`immutable`.

> It seems to me that there are two things that const (as a 
> parameter) means:
> 1. The caller guarantees they will not change the value
> 2. The callee guarantees they will not change the value

Point 1 is already wrong. `const` means the callee won’t change 
values through accessing this value. The callee may change the 
very same value through another, mutable reference.

Point 2 only applies to `immutable`, which also includes point 1.

> This gets really interesting with immutable, but even with 
> strings, having to cast char[] to const(char)[]. If the callee 
> is okay getting a char[] that is not const, const should 
> fulfil; the same need, but since const means two things you 
> have to cast. It's not that big of a deal, but with immutable 
> it can be.

Anything can be converted to `const` implicitly (except `shared`, 
which does not interact with `const`). That is for the simple 
reason that `const` simply forgets and only has outward 
guarantees.

> ```d
> import std.stdio;
>
> public void main()
> {
>     immutable(char[]) world = "world";
>     bongo(world);
> }
>
> void bongo(char[] x)
> {
>     writeln("Bongo " ~ x);
> }
> ```
>
> bongo could guarantee that it does not mutate the data, but it 
> doesn't want to require the caller to pass in immutable data 
> because it doesn't care if the data is mutated by the caller. 
> That's the caller's issue. So what should it put for the 
> modifier?

`const`; also `inout` would work:

```d
void bongo(const(char)[] x);
// or
void bongo(inout(char)[] x);
```

> I feel like final is one of these cases where it's half of a 
> guarantee. You could put final on bongo and it could be smart 
> enough to see that it can pass immutable data or not.
>
> This is one area that really tripped me up when I was trying to 
> write multi-threaded code. I had a bunch of data that was 
> initialized and then was effectively immutable from then on. 
> However, it was very unintuitive that I had to copy to a new 
> memory location.

In D, mutable implicitly converts to immutable if the value is 
unique. If you write a function that returns a `LinkedList!T` 
(random non-trivial example type, one with indirections) and that 
function is `pure` and has no parameters through which parts of 
the returned list could also be exposed (the easiest way to 
guarantee this is making parameters `const` or `immutable`), the 
returned list is unique. The caller may assign the returned list 
to an immutable-type variable.

---

Nothing of this is related to `final`. The `final` qualifier 
would be `const`, but only applies to the first layer of 
indirection. At ~10 years ago, I realized that a template `Final` 
for `Final!T` can’t really work with struct types. If a `T` 
member function is not qualified, it can mutate the first layer 
of indirection and thus is out for `Final!T`, but requiring a 
member function to be `const` to be called for a `Final!T` object 
is also wrong as it asks too much.

For a `final(int[])`, you could re-assign the elements, but not 
append to it.


More information about the dip.ideas mailing list