Without D1-style operator overloads, the `in` operator for classes doesn't make sense

Don prosthetictelevisions at teletubby.medical.com
Mon Jan 6 17:25:44 UTC 2020

On Monday, 6 January 2020 at 14:14:35 UTC, Steven Schveighoffer 
> On 1/6/20 6:16 AM, Ontonator wrote:
>> On Monday, 6 January 2020 at 10:55:15 UTC, Don wrote:
>>> In DMD 2.088.0, the D1 operator overloads were deprecated, in 
>>> favour of the D2 syntax which has been available for years.
>>> [...]
>> I’m on my phone right now, so I can’t check, but could you 
>> emulate a virtual operator by creating a virtual method (e.g. 
>> `contains`) which is called from the operator and which child 
>> classes could override? It isn’t as ergonomic, but it should 
>> work.
> In fact this is exactly why this change was allowed (and this 
> exact problem was discussed):
> class C
> {
>    int* opIn(string key); // virtual
>    auto opBinaryRight(string op : "in", X)(X key) // wrapper
>    {
>        return opIn(key);
>    }
> }
> So when/if the D1 operators get removed, you put this wrapper 
> in, and it works.

Indeed, you have implement something like that to support the 
'in' syntax on any class.
But it's only a partial workaround.
It's an ugly hack, and since there's no way to do it without 
breaking other code (see below), why would you even do that?
You are better off just creating a normal function like 
`contains`, the same as you would do in C++.

Note that I am singling out opIn and opCat/opCatAssign. These are 
fundamentally different from the normally binary operators.
A large part of the motivation for creating the D2-style 
overloads was the recognition that the code for '+' and '-' is 
almost always the same. Even '*', '/', '<<', will often share 
code with '+'. Merging them gets rid of a lot of code duplication.

But that isn't true for 'in' and '~'. We've made them 
syntactically similar, but semantically they are unrelated and 
they would normally share no code with the arithmetic and logical 
operators. In fact it's pretty rare for an object to define both 
'+' and 'in', for example.
They are much more closely related to opIndex and opSlice.

('in' is semantically similar to opIndex, in many ways).

> In fact, you can easily make a mixin template that will 
> implement all the D2 style operators when you have defined the 
> D1 style ones.

That's blocked as a direct migration path, because deprecation 
warnings are generated if you use the D1 names. (And no, you 
cannot turn off deprecation warnings, that turns off *all* 
deprecation warnings).

So you have to use names which are different from the D1 names. 
That creates a new problem. If you've deployed this as a library 
(which is very likely in collection classes!), anybody who has 
derived a class from your library class, and overridden opIn, now 
needs to rename their opIn to use your new name. You'd like to 
create a deprecation message to tell them how to do this, but is 
there even a way to do that?

(Incidentally, this is a illustration of why a deprecation 
process can make things much, much worse than directly removing 

In practice, support for the 'in' operator in classes has been 
dropped. Pretty much silently.

This is a major breaking change, it's not discussed in the 
changelog (the changelog advocates a mechanical substitution 
which doesn't work).

To make matters worse, there was no stability branch before this 
release. So to get critical security/wrong code bugfixes, you 
need to make this complicated change that propagates to code you 
don't even control.

I think we've made a mistake here.

More information about the Digitalmars-d mailing list