head const (again), but for free?
Q. Schroll
qs.il.paperinik at gmail.com
Mon Jan 11 19:05:13 UTC 2021
On Monday, 11 January 2021 at 17:15:39 UTC, Ola Fosheim Grøstad
wrote:
> So, I find that I increasingly miss head-const for immutable.
> And I understand that people don't want yet another way to
> modify the access patterns for a type, but maybe it is possible
> to avoid that, and do both.
>
> So here is the idea:
>
> Introduce "readonly" as head-immutable. That means that all
> values in an immutable array are immutable, including the
> address of reference, but not the objects being pointed to.
I tried implementing std.experimental.Final properly for structs.
(For arrays, pointers and classes, Final could work well: The
semantics of the operations of pointers and arrays are known and
classes are reference types.) So, here are my thoughts.
Your readonly is near useless if you really want it to mean "head
immutable" and not "head const". The value immutable provides is
due to being transitive. "Head immutable" is unnecessary
restrictive and has low guarantees.
For example, a head_immutable(int*)* cannot bind a int**
variable, because immutable and mutable are referentially
incompatible; head_const(int*)* can bind int** the same way
const(int*)* can.
For that, I'd suggest `final` as the name. It's already a keyword
and that's what it means on the first level. The difference to
Java would be that it's not (Java's equivalent of) a storage
class, but a type constructor. There would be final(int*)* which
looks funny through Java eyes, but I think it could work.
One of the problems std.experimental.Final has: Say T is a struct
(or union). On a Final!T object, you cannot call mutable or
immutable methods, only const methods. But even const methods are
too restrictive. If T has indirections, like a `int* ptr` member,
mutating *ptrof a Final!T object would be fine. That's something
a template cannot express.
So, final must also become a function attribute, too.
If you go with that DIP, it will probably be rejected because
final would have to be implemented and maintained, while its
application is very limited. There's probably a reason D went
from D1 to D2 trading head const for transitive const.
final as a type constructor has only value on a middle part of
indirection:
1. final(int**) can be treated as an int** except when
referencing. The only gain is that you don't accidentally assign
it.
2. final(int)** is the same as const(int)**.
3. Only final(int*)* is actually different from anything the
vanilla language provides. What you'd have to argue is, how
valuable is it? While final(T*) can easily be replaced by a
struct template Ref!T (same goes for T[] and reference types),
final(T) is interesting for structs T with indirections.
More information about the Digitalmars-d
mailing list