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