Operator overloading revisited

Mark S. Miller erights at google.com
Sun Jun 28 13:24:05 PDT 2009

On Sun, Jun 28, 2009 at 7:05 AM, Christian Plesner Hansen <
christian.plesner.hansen at gmail.com> wrote:

> Following the decimal discussion where values came up I looked for
> information on that.  The most substantial I could find was Mark's
> operator overloading strawman.
> I think the current strawman has a number of weaknesses.  It relies on
> the (hypothetical) type system which makes it fundamentally different
> from normal method dispatch.

Are you referring to <
https://mail.mozilla.org/pipermail/es-discuss/2009-January/008535.html>? It
starts with:

 Define a new nominal type named, let's say, "Operable". (I'm not stuck on
>> this name. Perhaps "Numeric"?) We don't yet know what nominal type system
>> Harmony will have, so for concreteness, and to separate issues, let's use
>> "instanceof" as our nominal type test, where only functions whose
>  [...suggested restrictions on when this test applies, but all without
presuming any "type"s beyond what ES5 already supports...]

I'm glad you started with one of the motivating problem cases: "+". The
reason I conditioned the new overloadings on Operable is to handle the "+"
case, which, to be upwards compatible, must continue to .toString() on
legacy non-number objects. One of Sam's earlier decimal proposals ran
aground on this "+" problem. IIUC, your proposal could deal with the legacy
"+" problem by duck typing on "this+" and "+this"? This may be adequate.

My strawman was written without any concept of value types at the time. In
committee, Waldemar then raised the "===" issue. "===" currently has the
great virtues that it is a reliable equality test that does not run user
code during the comparison, and whose veracity does not depend on user code.
NaN and -0 aside, if x === y, then they are observably indistinguishable.
Any occurrence of that value of x in a computation state can be substituted
for any occurrence of that value of y in that computation state without
effecting the semantics of that state. We did not want to lose those
virtues, so we do not want to allow user defined abstractions to overload
===. Neither do we want

    Point(3, 5) === Point(3, 5)

to be false. AFAIK, the only way to reconcile these goals is to introduce
value types. In earlier internal email I summarized the value-type thinking

* start with something like my operator-overloading-by-double-dispatch
* but tie the ability to respond to operators to being a value -- an
  instance of a value type/class.
* A value is necessarily frozen and compares "===" based on
  cycle-tolerant pairwise "===" comparison of named properties
  [missing from earlier email: as well as [[Prototype]] and [[Class]]
* Therefore, a value is indistinguishable from a shallow copy of the
  same value. Strings are a precedent.
* Values would be able to overload "==" but not "===".
* "===" would remain reliable and not cause user code to run during
  the comparison.

It seems to me that the Operable-test and value-type issues are orthogonal
to the core suggestion of your email: whether operator dispatch is based on
Smalltalk-like double dispatch, in which the left side is asymmetrically in
charge, or on mutual agreement which is indeed symmetric. The first bullet
could be changed to your proposal without affecting the rest of the
value-type issue.

I note that your symmetric suggestion avoids the problem of most other
symmetric overloading systems, like Cecil, of diffusion of responsibility.
Since your's only proceeds under mutual agreement, both operands are
responsible for the result. Unfortunately, this means that new libraries
like Point, in order to allow "pt + number" or "number + pt", must in
practice monkey patch Number.prototype to know about these new overloads.
Should Number.prototype in that frame (global object context) already be
frozen to prevent such monkey patching, then this Point extension can no
longer be loaded into that frame.

By comparison, the cool thing about double dispatch is that new types can
arrange to work with old types without monkey patching, given only that the
old types engage in the generic enabling default behavior and that the new
types have explicit cases for the old types it knows about and wishes to
work with. Might there be a way to combine the modular extension virtues of
double-dispatch with the symmetric-responsibility virtues of your proposal?
I don't know. But if so, that would be awesome!

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20090628/c602114b/attachment-0001.html>

More information about the es-discuss mailing list