On Saturday, 20 December 2014 at 21:25:28 UTC, Andrei
Alexandrescu wrote:
On 11/2/14 6:57 AM, IgorStepanov wrote:
And there is dispute about is expression: see
http://forum.dlang.org/thread/[email protected]?page=5
OK, time to get this approved.
First, the current DIP doesn't seem to address this:
Walter and I would agree to making the presence of BOTH alias
this
and opDispatch a compile-time error. That would break existing
code
but not change semantics silently.
Far as I remember it was left to the discussion. Nobody objected
to this issue, thus we may accept it. I think.
Any thoughts on this? Currently opDispatch gets priority over
alias this, see lookup step 3 in section "Semantics" of
http://wiki.dlang.org/DIP66. That's problematic because it puts
opDispatch in _between_ "normal" subtyping via inheritance and
alias this, which is supposed to be just as solid as
inheritance.
I think the principled solution is to combine steps 2 and 4
into step 2, i.e. alias this is as strong as inheritance. Any
ambiguous symbols would be rejected.
The second possibility, less principled but probably practical,
would be to swap steps 3 and 4. That way alias this has juuust
a teensy bit a lower status than regular inheritance.
It looks nice, but it can greatly break the existing code. I
suggest a postpone this issue and discuss the semantic order in a
separate discusson/
The simplest thing (which Walter favors) is to make the
presence of both opDispatch and alias this a compile-time
error. That would break only a teensy amount of code if any,
and would give us time to investigate the best approach when
compelling use cases come about. So I suggest we move forward
with that for this DIP.
Regarding the is-expression controversy in
http://forum.dlang.org/thread/[email protected]?page=5:
First off, is(S : T) is a subtyping test - is S a non-proper
subtype of T, or not? (Non-proper or improper subtyping: S is
allowed to be identical to T). "alias this" is a mechanism that
introduces subtyping. It follows that subtyping introduced via
"alias this" must be detected with is-expressions.
Now, you give an example of subtyping where one or more two
objects of the same supertype may be reached through two or
more different paths. This is a well-known problem in subtyping
(known as diamond hierarchy or repeated inheritance).
In the case of "alias this", different objects of the same type
may be reachable (or at least the compiler is unable to tell
statically whether the objects are distinct or not). A correct
but hamfisted solution would be to sever the subtyping
relationship whenever the same type is reachable through
multiple paths.
The versatility of "alias this", however, suggests a better
solution: if T is indirectly reachable as a supertype of S
through more than one path and the subtyping is either tested
(by means of an is-expression) or effected (by means of an
implicit conversion), the compiler should issue a compile-time
error asking the user to define an "alias this" DIRECTLY inside
S, which takes precedence over indirect reachability and
informs the type system which T of the several reachable ones
is needed.
Please let me know of any thoughts. Thanks!
Summing up.
There are three way to process is(D: B) where D may be converted
to B in several ways.
1. is(D: B) should return false: D is not subtype of B now.
2. is(D: B) should return true: D is subtype of B anyway.
3. is(D: B) should raise an error: let the user decide what he
wants.
I strongly aganist the first way. It means that is(D: B) may
absorb the real error, if it happens.
Now only two construction in D may absorb errors:
is(typeof(something)) and __traits(compiles, anything)).
I say "absorb" when compiler see the error, ignores it and
changes way of compilation:
static if (<noErrors>)
<correct branch>
else
<error branch>
This situation may cause strage errors, code hijacking and other
bad things, thus user should has a possibility to keep track of
such cases.
is(typeof(something)) and __traits(compiles, anything)) is a
special constructions to error handling and user and everyone
understands what is expected.
is(D: B) is trusted construction and it can't create problems
now. Let's leave it so.
The second way is better, I think. It doesn't absorb the error,
it skip error but doesn't change the compilation way.
Error will be raised anyway when compiler will process code which
use this casting.
void foo(D)(D obj) if (is(D: Base)) // compiler will skip the
error here...
{
Base b = obj; //... but it will raise the error here.
}
The third way is correct too, I think. It raises error earlier,
but I changes current `is` semantic. AFAIK, `is` doesn't raise
errors now.
the compiler should issue
a compile-time error asking the user to define an "alias this"
DIRECTLY
inside S, which takes precedence over indirect reachability and
informs
the type system which T of the several reachable ones is needed.
That means that user should may override inherited alias this
declarations:
struct A
{
alias i this;
int i;
}
struct B
{
alias i this;
int i;
}
struct C
{
alias a this;
alias b this;
alias b.i this; //override inherited alias int this.
A a;
B b;
}
It was implemented in my first implementation, but AFAIR you
suggested delay it for postpone this feature and introduce it
later. Thus now I remove this option from PR and DIP, but I may
revert it back.
P.S. sorry for big latency, it will take place within a couple of
months, but I will do this work anyway.