On Jun 30, 2009, at 1:04 PM, Mark S. Miller wrote:

On Mon, Jun 29, 2009 at 10:26 PM, Brendan Eich <[email protected]> wrote:
On Jun 29, 2009, at 11:55 AM, Mark S. Miller wrote:

 Let's try a reductio ad absurdum.

This doesn't settle anything since there is no "for all" claim in Chambers, et al.'s words cited above. You can't reduce to an absurdity something that according to its own proponents does not apply in all cases.

A fine counterargument if you can distinguish which cases it does and doesn't apply to. The syntactic distinction "operators" seems silly. I'm glad you seem to include Math.atan2(), as that gets us beyond the silly syntactic distinction.

Did you check out any of the papers I linked? They talk about non- silly, not-only-about-syntax use-cases (~20% by function population, I recall Chambers writing somewhere) that include (dyadic) operators, math functions, event sourcing/sinking, a few other multi-party situations with symmetric relations and no obvious primary receiver.

I don't think there was ever a silly syntactic distinction about operators in anything I've written, recently or in past years, on this list or elsewhere. Value types want operator and literal extensibility. The operator part is one of N use-cases for multimethods IMHO. That's all.


It seems to me you are the one making a universal claim, for OOP methods as the one true way, which could be argued to lead to absurd (or just awkward) conclusions such as double dispatch (hard for V8, easy for TraceMonkey :-P) and "reverse+", "reverse-", etc. operator-method bloat.

Contradicted by the next text of mine you quote:

But not by your double-dispatch proposal for operators. That's one way to add extensible operators. The other is to add multimethods. I don't know of a distinct and canonical third way.

Talking about good old functions doesn't help rule out multimethods, indeed it raises them as an option since dyadic operators appear to be functions, not receiver-targeted methods.

To be honest I wasn't sure what your point about good old bar(y, z) was, except to say that bar was the locus of responsibility (Tucker noted this callee-is-responsible case too). Again this does not argue against multimethods.

Likewise blame systems are great (costly, PLT Scheme users typically add contracts at module boundaries to reduce overhead, intra-module calls can be fast at the cost of losing the blame-carrying wrappers), but I don't see how they require w or bar to bear singular responsibility.

Multimethods really are multiple functions inhabiting bar, dispatched by a "most specific argument type" matching algorithm, which can fail but when it succeeds, invokes a single function. That's the responsible function, whatever bar multimethod it is. ;-)


I was not arguing that w.foo(x) is always better than bar(y,z). Rather, I was saying that currently neither violate locality of responsibility. In these two expressions, w and bar are the respective responsible parties.

Ok, that's what I thought. Now consider y % z where % is defined as in Christian's proposal, amended by Allen's suggestion that the spec not rely on non-internal, prototype-delegated properties. I'll use obj[[internalId]] to denote internal property access and ^^ for intersection:

let lst :List = y[["this%"]] ^^ z[["%this"]];
if (lst.length == 0) throw TypeError("% not defined on operands");
if (lst.length != 1) throw TypeError("ambiguous % operation");
return lst[0](y, z);

What party is responsible here, given that the only way any pair of internal properties of the form y[["this%"]] and z[["%this"]] get defined (on constructor prototypes, per Allen's followup) is by Function.defineOperator being called with both y and z's "types" (constructors).

So (to repeat a point from my last post) we don't actually need (or want) internal instance properties to keep book -- we could use a separate lexically scoped two-dimensional mapping for the % operator indexed by "types" (see instanceof).

If the only observable difference between using internal instance properties and using a separate lexical operator matrix is that you claim Object.freeze(Point.prototype) should cause

  Function.defineOperator('+', Point, Number, pointPlusNumber);

or similar calls passing Point fail, then I side with Allen where he wrote:

"I wouldn't want to describe or specify these per object (or per prototype) operator lists as properties. Too much baggage comes with that concept. It'd just make them another internal characteristic of objects. As already mentioned, if you think of them this way there shouldn't be any interactions with Object.freeze or any other existing semantics other than whatever we might choose to explicitly define."

But that's no surprise, because I'm in favor of letting Carol define multimethods for types purveyed by Alice and Bob, where Alice and Bob didn't foresee the combination Carol found useful.

I don't see how this is "irresponsible". If Alice and Bob freeze prototypes, Carol could still hack wrappers, in the absence of multimethods. If Alice and Bob don't want to provide their types to Carol, they can hide them otherwise. Hiding != Freezing.


Were we to adopt multimethods, where atan2 somehow gets enhanced by all imported modules defining overloadings of atan2 in that scope, what is the value of the atan2 variable in that scope?

A multimethod.


If it is a function composed of all lexical contributors, what happens when this function is passed as a value and then used in a different scope?

Multimethods [*] are first-class values, sets of associated type- specific statically scoped functions. No dynamic scope, of course.

You define a multimethod, let's say with new syntax or new Function.defineMultimethod or whatever strawman API you like (trade- offs about early error checking left for later discussion). So atan2 is not just a "good old function". It's something new, an overloaded function that "has" multimethods (the ES4 proposal, FWIW, had new syntax like |generic function atan2(x, y);| to get things started).

(I don't mean to belabor this point, but I do want to stress that plain old functions can't be confused for multimethods and unintentionally mixed together. JS replaces any previous binding when processing a function declaration. This behavior would of course continue, but if we add multimethods, then we have the choice to make it an error to define a multimethod whose name is already bound to any value, or to a plain old function, or whatever seems best.)

Anyway, code with access to the distinguished multimethod's lexical binding then would add functions to it by denoting it in that scope or using a reference to it passed to another function which could add functions to the multimethod.

This is observably equivalent as far as I can tell to how Christian's Function.defineOperator would seem to work -- except of course that Function.defineOperator mutates internal "this+" and "+this" properties given a first argument of '+', whereas a Function.addMultimethod(atan2, Complex, Rational, function (c, r) {...}) API would take an explicit reference to the multimethod being augmented.

(EIBTI! :-P)

Finally, anyone with a reference (found in the lexical scope as the basis case, or passed to other functions to introduce them to the atan2 multimethod) can call atan2 on the several combinations of argument types defined in it. Dispatch works as in Dylan or Cecil, if we want to get fancy with subtypes; or simply as in Christian's proposal (zero inheritance FTW ;-).

Short on time, so I should stop here and ask what seems unclear or wrong.

/be

[*] The "method" name may mislead, but Java static methods don't have a distinguished receiver, so we can cope. I'm using the "multimethod" name instead of "generic method" or "generic function" -- generic methods or generic functions cause confusion with "generics", and with plain old JS functions that make few assumptions about argument types.


_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to