Generic operator overloading for immutable types?

Steven Schveighoffer via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Jun 13 18:47:21 PDT 2017


On 6/13/17 7:51 PM, ag0aep6g wrote:
> On 06/14/2017 12:45 AM, Steven Schveighoffer wrote:
>> No, the fact that immutable implicitly casts to const(inout) is a
>> special property enabled by the knowledge that immutable data can
>> NEVER change, so it's OK to assume it's (at least) const for all
>> references. The same cannot be true of const or mutable.
>
> In other words: Immutable can't ever become mutable, at most it can
> become const.
>
> In the same vein: Mutable can't ever become immutable, at most it can
> become const.
>
> I don't see the fundamental difference. Mutable and immutable act very
> much alike. They're just on opposing ends of the scale.

The fundamental difference is that const and immutable share a 
characteristic that mutable doesn't -- you can't mutate the data.

The reason const(inout) works is because const(immutable) evaluates to 
just immutable.

const(<mutable>) just evaluates to const. In this way, mutable is less 
"special" than immutable.

>> This cannot work, because g() has no idea what the true mutability of
>> x is. inout is not a template. This is why you can't implicitly cast
>> inout to anything except const, and you can't implicitly cast anything
>> to inout.
>
> I don't follow. `inout const` doesn't need a template to do its thing.

Right, it's because the point at which the inout is "unwrapped" (i.e. at 
the point of return), it either becomes const or immutable. Inout 
functions can compile oblivious to how they will be called because the 
caller can completely determine the type that should be returned. It was 
the impetus to create inout in the first place -- why generate all these 
functions that do the same thing, just to change the return type, let 
the caller figure it out.

> I'm not sure what you mean about implicit conversions. Obviously, an
> inout result matches the corresponding inout argument(s). Doesn't matter
> if that's an implicit conversion or whatever.

There is actually a difference. inout wraps one and exactly one type 
modifier. The table in that function shows what the modifier is 
depending on your collection of mutability parameters.

If ALL of them are the same, it becomes that same thing.

If any are different, you use the table to figure out. Most differences 
become const, some special cases become const(inout). This special case 
is solely to allow a function that takes both immutable and inout to 
potentially return immutable instead of just const.

> Now, this code could be made to work:
>
> ----
> bool condition;
> auto f(inout int* x)
> {
>     int* y; /* mutable now */
>     return condition ? x : y;
> }
> void main()
> {
>     int* r1 = f(new int);
>     const r1 = f(new immutable int);
> }
> ----
>
> Mutable in, mutable out. Const in, const out. Immutable in, const out.
> You can't get immutable out, because y is mutable, and it cannot become
> immutable. Same principle as above. You never go from mutable to
> immutable or the other way around, so everything's fine, no?

The soundness of the function above seems good, but I don't know how to 
reason about the return type of f. Because mutable has no type modifier, 
it's hard to imagine doing this without one. And any time I think about 
how to define it, it breaks down. I can't imagine const(blah(T)) 
evaluating to mutable, no matter what blah is called, or how it works.

Literally the ONLY place this would be useful is for inout functions, 
and Andrei pretty much has declared that inout should be completely 
stricken from Phobos/druntime in favor of templates. I can't imagine any 
leeway for another type modifier.

const(inout) literally was an afterthought observation that we could 
relax the rules for this one case and get a little more usability.

-Steve


More information about the Digitalmars-d-learn mailing list