|
View:
New views
20 Messages
—
Rating Filter:
Alert me
|
| < Prev | 1 - 2 - 3 | Next > |
|
|
Re: Operator overloading revisited> Costs that seem to leap out from Christian's mutual-asymmetric multimethod
> proposal: > > 1. Two lookups, "this+" and "+this", required. > 2. Intersection. > > (2) looks like a transformation or simplification of standard multimethod > Class Precedence List usage to find the most specific method. Any system > allowing multiple methods for a given operator will have something like it. > > (1) seems avoidable, though -- *assuming* it is not a phantom cost. We have > to deal with the symmetric vs. encapsulated issue, though. But notice that > Christian's proposal does not use "this" in the method bodies! The really nice thing about this from a cost standpoint is that it caches well. If changes to the operators understood by an object are reflected in the object's hidden class (to use v8 terminology) the same way changes to methods are, all the same caching techniques can be used. A global (hidden class, hidden class, operator)->function cache can be used to ensure that the full operator resolution only has to be done once for any operand type pair, after which the result can be fetched by a simple hash table lookup. Similarly, inline caching can ensure that the hash lookup only has to be done once for any monomorphic call site; megamorphic call sites, which I would expect were rare, would have to do a hash lookup but not full operator resolution. By the way, did you say "mutual-asymmetric multimethod" :-)? > Function.defineOperator in the proposal binds both "this+" and "+this". I > agree these should not be plain old properties. But then why spec them as > named internal properties of any kind? > > Suppose we had a generic function "+" (syntax TBD) to which methods > could be added for particular non-conflicting pairs of operand types. Then > the lookup would be for two types A and B, but this can be optimized under > the hood. See, e.g., Methods live on objects and their prototypes. Operators should too. Using a special internal property rather than plain old properties is fine by me but they should be directly associated with the objects involved. I'd argue why that is so important but I'd like to be sure I understand what you're proposing as an alternative. As I understand it you would associate the operators with the constructor functions similar to python's five minute multimethods. Is that correct? _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisited> Do have a look at Cecil. Multimethods probably can't get much better than
> that. I'm not anti-multimethods. If I was doing a language from scratch it would have multimethods. That doesn't mean that adding them to JS is a good idea, or that mentioning them would further my case. A side note: while cecil's model is nice and clean I wouldn't say that it was the platonic ideal of a multimethod model. For instance, I think it's reliance on implementation/representation types for dispatch rather than interface could be improved upon. But that's a whole other discussion. _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisited> Almost. It's actually a bit worse than that, since presumaby, Points want to
> be addable with Numbers: > > Point.prototype['+'] = function (that) { > if (that instanceof Point) { > return new Point(this.x + that.x, this.y + that.y); > } else if (that instanceof Number) { > // again ignoring the difference between numbers and Numbers > return new Point(this.x + that, this.y + that); > } else { > return that['reverse+'](this); > } > }; I find the use of instanceof problematic for several reasons. The connection between constructor functions and instances is fragile. Once you've created an instance you're free to change the constructor without those changes being reflected in the instance. What it means to be a Point can change throughout the lifetime of a program. Furthermore, instanceof is an implementation type query, not an interface or behavior type query. The limitations of implementation types compared with interface types are well understood. > Perhaps Complex(3, 5) === Complex(3, 5) is a better example? I really don't see it. What is the problem with two complex numbers being distinct object instances and strict equals reflecting that fact? If you want structural equality use ==, that's what it's there for. > Ignoring Operable, in your proposal, how would you handle the need to > preserve the legacy + behavior on legacy kinds of objects? The only other > possibility I foresee is duck typing on the presence of the 'this+' and/or > '+this' property names. That would work. Duck typing is built into the proposal already. It is folded into step 2 and 4 as a special case of bailing out if looking up the properties doesn't yield lists. If an internal property were used instead it would have to have a state that reflected "no operators are present", maybe the absence of the internal property, which would serve the same purpose. Instead of giving an error in step 2 and 4 you would fall through to the legacy behavior if either side didn't respond to operators. _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedOn Jul 1, 2009, at 12:48 AM, Christian Plesner Hansen wrote:
Adding "mutual" didn't help, did it? I was trying to capture the reciprocal "this+"/"+this" intersection requirement. But it still seems to me any such internal properties should constitute an unobservable specification/implementation device. Below you allow for internal properties, so presumably ("this+" in a) would *not* evaluate to true for (a + b) in your examples. So why require"this+" and "+this" bindings at all? Caching under the hood is still doable but it is an implementation detail.
Why should operators be methods? Binary operators have no distinguished receiver. Indeed your proposal has nice generic functions for addition, no |this| at all. So much for "methods". Saying that methods should "live" (be bound) somewhere when they do not use |this| to denote the receiver in which they are bound (directly or on the prototype chain) seems like even weaker dogma. Mark's double-dispatch is consistent with OOP. Your proposal seems to require reciprocal external property mutations on prototypes but only so the dispatch algorithm can do its simplified CPL thing. Why?
Function.defineOperator('+', Point, Number, pointPlusNumber);
Yes. /be _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedOn Jul 1, 2009, at 1:33 AM, Christian Plesner Hansen wrote:
>> Almost. It's actually a bit worse than that, since presumaby, >> Points want to >> be addable with Numbers: >> >> Point.prototype['+'] = function (that) { >> if (that instanceof Point) { >> return new Point(this.x + that.x, this.y + that.y); >> } else if (that instanceof Number) { >> // again ignoring the difference between numbers and Numbers >> return new Point(this.x + that, this.y + that); >> } else { >> return that['reverse+'](this); >> } >> }; BTW, Mark's code reminds me of a paper on tracing double-dispatch I've mentioned in past talks: http://www.ics.uci.edu/~franz/Site/pubs-pdf/ICS-TR-07-10.pdf The approach of double-dispatching on what you know and bottoming out in a reverse-foo call when you don't know the other operand's type to me looks like an application "when in doubt, use brute force" (Thompson). That can be an effective strategy, provided you're truly in doubt about better ways to solve the problem. But it is verbose and probably error prone. I much prefer something like your function pointPlusNumber(a, b) { return new Point(a.x + b, a.y + b); } Function.defineOperator('+', Point, Number, pointPlusNumber); function numberPlusPoint(a, b) { return new Point(a + b.x, a + b.y); } Function.defineOperator('+', Number, Point, numberPlusPoint); function addPoints(a, b) { return new Point(a.x + b.x, a.y + b.y); } Function.defineOperator('+', Point, Point, addPoints); to the if/else mess cited above (plus the reverse+ other half). How much sweeter could this be sugared? function "+"(a :Point, b :Number) { return new Point(a.x + b, a.y + b); } function "+"(a :Number, b :Point) { return new Point(a + b.x, a + b.y); } function "+"(a :Point, b :Point) { return new Point(a.x + b.x, a.y + b.y); } (Here the quoted operator names imply multimethod addition, where previously I used |generic function| to Tucker's enthusiastic +∞). A programming language should not necessarily require low-level coding from its users just to conserve dispatch mechanism. Users are the masters, we specifiers and implementors are the servants. Yes, adding a novel dispatch mechanism adds complexity and cognitive load for those learning the whole language. No, not everyone learns the whole language at once. Complexity can be contained if the new dispatch machinery is well-separated and provided it composes well. Done right, it's not going to burn any n00bs. Sure, we could leave out multimethods and use double dispatch. We could leave out operators altogether and make 'em eat functions! But the motivation here is usability, not theoretical computability. > I find the use of instanceof problematic for several reasons. Yeah, that's why I used "types" in scare-quotes. But for now can we defer the interface or behavior ("type" as a four-letter word) debate, and try to agree on multimethods vs. double dispatch? >> Perhaps Complex(3, 5) === Complex(3, 5) is a better example? > > I really don't see it. What is the problem with two complex numbers > being distinct object instances and strict equals reflecting that > fact? If you want structural equality use ==, that's what it's there > for. No, because for two numbers a and b, a == b <=> a === b. In fact for any two values, (typeof a == typeof b && a == b) <=> a === b. If we want to support usable numeric and similar "scalar" type extensions, we might want to preserve this logic. /be _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisited> This cuts both ways. A multimethod reifies that "global" (lexically scoped,
> rather) cache. Sure, I'm not saying it's unique to my proposal. I mention it because it's an important aspect to keep in mind when considering the cost of implementing it in practice. > Sorry, I kept typing "asymmetric" when I meant "symmetric". Tired > here (new baby at home :-)). Congratulations! And congratulations on firefox 3.5 too by the way! > Below you allow for internal properties, so presumably ("this+" in a) would > *not* evaluate to true for (a + b) in your examples. So why require"this+" I'm all for internal properties rather than "this+" and "+this". >> Methods live on objects and their prototypes. Operators should too. > > Saying that methods should "live" (be bound) somewhere when they do not use > |this| to denote the receiver in which they are bound (directly or on the > prototype chain) seems like even weaker dogma. Mark's double-dispatch is > consistent with OOP. Your proposal seems to require reciprocal external > property mutations on prototypes but only so the dispatch algorithm can do > its simplified CPL thing. Why? All things being equal I'd probably rather have operator functions outside objects or prototypes. But things are not equal. Relying on a connection between constructor functions and instances is fragile, as I mentioned in a previous message to Mark about instanceof. You can make changes to a constructor function that is not reflected in existing instances. You can make changes both to instances and constructors that disconnect the two completely. Constructor functions are implementation types (as opposed to interface or behavior types) which is inherently problematic. I wouldn't make this weak connection the cornerstone of operator dispatch. The connection between prototypes and instances is a much more direct one. If you change a prototype that change is reflected in the instances (modulo shadowing). If I want my type B to have all the same behavior as A I can mix all the methods of A into B's prototype and I will in effect have made B a subtype of A. If operators reside in A's prototype, in whatever form, I can do the same for operators. Whether or not A's '+' operator uses 'this', it's still a part of A's behavior and the most consistent place to put it is where A's other behavior is, its prototype. >> Using a special internal property rather than plain old properties is >> fine by me but they should be directly associated with the objects >> involved. > > Directly? You wrote "and their prototypes" above, and your > Function.defineOperator examples were passed constructors (from which to > mutate prototypes), not direct instances: Right, I could have been more explicit about that. I do mean including prototypes, they fall under "objects involved". _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedOn 2009-07-01, at 03:48EDT, Christian Plesner Hansen wrote:
> Methods live on objects and their prototypes. Only if you co-opt the word "method" to mean that. I would claim this is just shorthand for "instance method". There is also "class method" or "static method". There are other definitions (see below). On 2009-07-01, at 10:23EDT, Brendan Eich wrote: > How much sweeter could this be sugared? > > function "+"(a :Point, b :Number) { > return new Point(a.x + b, a.y + b); > } > > function "+"(a :Number, b :Point) { > return new Point(a + b.x, a + b.y); > } > > function "+"(a :Point, b :Point) { > return new Point(a.x + b.x, a.y + b.y); > } > > (Here the quoted operator names imply multimethod addition, where > previously I used |generic function| to Tucker's enthusiastic +∞). Indeed. Not to suggest paint, but I think the syntactic sugar of Dylan has a nice color: `a + b` is sugar for `\+(a, b)` `\+` is a generic function (of 2 arguments) to which you can add "methods". Methods are just syntactic sugar for specifying the branches of a dispatch algorithm (http://portal.acm.org/citation.cfm?id=236338.236343 ). [That is since `+` is not a legal identifier, you have to escape it to use it as an identifier, to name a generic function.] If "method" has to be used uniquely in Javascript as the word that means a computation attached to an instance, maybe we just need a new word to mean "dispatch element of a generic function". I would have said "generic function method", which (ambiguously) gets abbreviated to "method", and may be the source of objection? _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedOn Jul 1, 2009, at 8:14 AM, Christian Plesner Hansen wrote:
>> This cuts both ways. A multimethod reifies that "global" (lexically >> scoped, >> rather) cache. > > Sure, I'm not saying it's unique to my proposal. I mention it because > it's an important aspect to keep in mind when considering the cost of > implementing it in practice. All of us optimizing JS VM hackers agree, you will have no failure to object to over-specification that deoptimizes or rules out important optimizations. But the spec should not overspecify in favor of a particular optimization either, since what is observable other than performance won't differ between the overspecified standard and a hypothetical "just right" spec. Of course getting it "just right" is hard. >> Sorry, I kept typing "asymmetric" when I meant "symmetric". Tired >> here (new baby at home :-)). > > Congratulations! And congratulations on firefox 3.5 too by the way! Thanks. Helps to keep things in perspective ;-). > I'm all for internal properties rather than "this+" and "+this". How about an alternative spec that does not preclude hidden class optimizations based on + overloadings whether by multimethods or "this +"/"+this"? I suppose we can't defer the "type" issue, though... > Relying on a connection between constructor functions and instances is > fragile, as I mentioned in a previous message to Mark about > instanceof. You can make changes to a constructor function that is > not reflected in existing instances. Constructor properties don't show up in instances, so I'm not sure why this is a problem. > You can make changes both to > instances and constructors that disconnect the two completely. Yes, with extensions such as writable __proto__ -- but that's something we are trying to get rid of. Likewise, for user-defined function foo, foo.prototype is writable -- but not so for built-in constructor functions, and not so for classes as sugar (more below). Point so far is not to disagree but to cite cases showing a mix of higher- and lower-integrity connections between constructors and their prototypes. > Constructor functions are implementation types (as opposed to > interface or behavior types) which is inherently problematic. I > wouldn't make this weak connection the cornerstone of operator > dispatch. I suppose Object.freeze could be seen as a "fix" here, but it is advisory -- therefore not used when most needed. The original "classes as sugar" sketch Mark did to general agreement at Oslo included freezing the class object (constructor function) *and* bind the class name as a const (neither writable nor configurable in the binding object or frame). So a higher-integrity "fix" would be to make classes as proposed for Harmony (details still in flux, I think) the cornerstone for operator dispatch. I think this is the plan for "value types", based on discussion at the last TC39 meeting. Mark will let us know if he disagrees. > The connection between prototypes and instances is a much more direct > one. If you change a prototype that change is reflected in the > instances (modulo shadowing). If I want my type B to have all the > same behavior as A I can mix all the methods of A into B's prototype > and I will in effect have made B a subtype of A. If operators reside > in A's prototype, in whatever form, I can do the same for operators. You could, but your proposal nicely avoided noisy ".prototype" expressions all over, as Allen suggested in reply. Adding Complex, Decimal, Rational, Point, etc. for most users implies, nay cries out for being able to use these names without having to suffix them with ".prototype". If the binding of the constructor name in its object or frame *and* the binding of its 'prototype' property are locked down due to how classes as sugar work, then we should be able to rely on the constructor / prototype relationship. > Whether or not A's '+' operator uses 'this', it's still a part of A's > behavior and the most consistent place to put it is where A's other > behavior is, its prototype. Could be, or one could use an optimized multimethod matrix, or some other implementation approach. Again I do not see why the spec should dictate observable prototype properties, internal or otherwise. If it just uses this model but keeps it concealed, then I'm ok with it provided we don't close the door on multimethods prematurely. If we want multimethods, then we might prefer the spec to go a different direction and avoid internal "this+"/"+this", etc. Not sure this helps get a operators (and only operators) proposal together. Good discussion, though! /be _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedOn Jun 28, 2009, at 7:05 AM, Christian Plesner Hansen 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. It uses double dispatch for all > user-defined operators which makes optimization difficult. It is > asymmetrical: the left hand side always controls dispatch. > > I'd like to propose an alternative approach that avoids these > problems. You could call it "symmetric" operator overloading. When > executing the '+' operator in 'a + b' you do the following (ignoring > inheritance for now): > > 1: Look up the property 'this+' in a, call the result 'oa' > 2: If oa is not a list give an error, no '+' operator > 3: Look up the property '+this' in b, call the result ob > 4: If ob is not a list give an error, no '+' operator > 5: Intersect the lists oa and ob, call the result r > 6: If r is empty give an error, no '+' operator > 7: If r has more than one element give an error, ambiguity > 8: If r[0], call it f, is not a function give an error > 9: Otherwise, evaluate f(a, b) and return the result. > > The way to define a new operator is to add the same function to the > 'this+' list of the left-hand side and the '+this' list on the > right-hand side. This would probably be handled best by a simply > utility function, say Function.defineOperator: > > function pointPlusNumber(a, b) { > return new Point(a.x + b, a.y + b); > } > > Function.defineOperator('+', Point, Number, pointPlusNumber); > > Using this approach it's easy to extend symmetrically: > > function numberPlusPoint(a, b) { > return new Point(a + b.x, a + b.y); > } > > Function.defineOperator('+', Number, Point, numberPlusPoint); > > In this case you need two different functions, one for Point+Number > and one for Number+Point, but in many cases the same function can be > used for both: > > function addPoints(a, b) { > return new Point(a.x + b.x, a.y + b.y); > } > > Function.defineOperator('+', Point, Point, addPoints); > > This approach is completely symmetric in the two operands. Operator > dispatch is fairly similar to ordinary method dispatch and doesn't > rely on a type system. Programmers don't have to manually implement > double dispatch. It my be a bit more work to do lookup but on the > other hand it is straightforward to apply inline caching so often > you'll only need to do that work once. It can be extended to deal > with inheritance -- you might consider all operators up through the > scope chain and pick the most specific one based on how far up the > scope chain you had to look. > > It may look a bit foreign but I think it has a lot of nice properties. > Comments? It seems like once you do the defineOperator calls you described above, and assuming they work for primitive numbers and not just Number objects, then adding two numbers would require two property lookups, unless there is an exception for operating on two primitive values. Regards, Maciej _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedI think we could get away with making an exception there. The spec
algorithm for how to execute '+' would check if the operands were primitive numbers before attempting user-defined operators. There are already cases where changes to Number.prototype don't propagate to primitive numbers, leading them to behave differently. For instance: Number.prototype.valueOf = function () { return 4; }; print(1 + 1); // yields 2 print(1 + new Number(1)); // yields 5 On Thu, Jul 2, 2009 at 11:53 AM, Maciej Stachowiak<mjs@...> wrote: > > On Jun 28, 2009, at 7:05 AM, Christian Plesner Hansen 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. It uses double dispatch for all >> user-defined operators which makes optimization difficult. It is >> asymmetrical: the left hand side always controls dispatch. >> >> I'd like to propose an alternative approach that avoids these >> problems. You could call it "symmetric" operator overloading. When >> executing the '+' operator in 'a + b' you do the following (ignoring >> inheritance for now): >> >> 1: Look up the property 'this+' in a, call the result 'oa' >> 2: If oa is not a list give an error, no '+' operator >> 3: Look up the property '+this' in b, call the result ob >> 4: If ob is not a list give an error, no '+' operator >> 5: Intersect the lists oa and ob, call the result r >> 6: If r is empty give an error, no '+' operator >> 7: If r has more than one element give an error, ambiguity >> 8: If r[0], call it f, is not a function give an error >> 9: Otherwise, evaluate f(a, b) and return the result. >> >> The way to define a new operator is to add the same function to the >> 'this+' list of the left-hand side and the '+this' list on the >> right-hand side. This would probably be handled best by a simply >> utility function, say Function.defineOperator: >> >> function pointPlusNumber(a, b) { >> return new Point(a.x + b, a.y + b); >> } >> >> Function.defineOperator('+', Point, Number, pointPlusNumber); >> >> Using this approach it's easy to extend symmetrically: >> >> function numberPlusPoint(a, b) { >> return new Point(a + b.x, a + b.y); >> } >> >> Function.defineOperator('+', Number, Point, numberPlusPoint); >> >> In this case you need two different functions, one for Point+Number >> and one for Number+Point, but in many cases the same function can be >> used for both: >> >> function addPoints(a, b) { >> return new Point(a.x + b.x, a.y + b.y); >> } >> >> Function.defineOperator('+', Point, Point, addPoints); >> >> This approach is completely symmetric in the two operands. Operator >> dispatch is fairly similar to ordinary method dispatch and doesn't >> rely on a type system. Programmers don't have to manually implement >> double dispatch. It my be a bit more work to do lookup but on the >> other hand it is straightforward to apply inline caching so often >> you'll only need to do that work once. It can be extended to deal >> with inheritance -- you might consider all operators up through the >> scope chain and pick the most specific one based on how far up the >> scope chain you had to look. >> >> It may look a bit foreign but I think it has a lot of nice properties. >> Comments? > > It seems like once you do the defineOperator calls you described above, and > assuming they work for primitive numbers and not just Number objects, then > adding two numbers would require two property lookups, unless there is an > exception for operating on two primitive values. > > Regards, > Maciej > > es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisited> Likewise, for user-defined function foo, foo.prototype is writable -- but
> not so for built-in constructor functions, and not so for classes as sugar > (more below). All JS code currently in existence is based on user-defined functions. For me that is the only case worth considering. That might explain the differences in our views. I'm not a language revolutionary, I prefer gradual evolution. > The original "classes as sugar" sketch Mark did to general agreement at Oslo > included freezing the class object (constructor function) *and* bind the > class name as a const (neither writable nor configurable in the binding > object or frame). > > So a higher-integrity "fix" would be to make classes as proposed for Harmony > (details still in flux, I think) the cornerstone for operator dispatch. I > think this is the plan for "value types", based on discussion at the last > TC39 meeting. Mark will let us know if he disagrees. If you have the option to use a model that works with the existing language and that integrates well even (that's subjective of course) why would you limit it to classes or values? The reliance of these new features on each other will just lead them to become a language-within-a-language, rather than an evolution of the existing language. > You could, but your proposal nicely avoided noisy ".prototype" expressions > all over, as Allen suggested in reply. That was a typo. > Adding Complex, Decimal, Rational, Point, etc. for most users implies, nay > cries out for being able to use these names without having to suffix them > with ".prototype". If the binding of the constructor name in its object or > frame *and* the binding of its 'prototype' property are locked down due to > how classes as sugar work, then we should be able to rely on the constructor > / prototype relationship. If people can live with defining their methods as Foo.prototype.bar = function () { ... } I think they can live with writing .prototype when defining operators. In fact I like the uniformity that you have to use the prototype in both cases. Note that library users won't see this, only the library implementer. In any case, what users will "cry out for" is speculation. >> Whether or not A's '+' operator uses 'this', it's still a part of A's >> behavior and the most consistent place to put it is where A's other >> behavior is, its prototype. > > Could be, or one could use an optimized multimethod matrix, or some other > implementation approach. Again I do not see why the spec should dictate > observable prototype properties, internal or otherwise. If it just uses this > model but keeps it concealed, then I'm ok with it provided we don't close > the door on multimethods prematurely. JS allows introspection and self-modification and both are widely used and cited as advantages of the language. I would want to extend that to cover operators as well. Having an internal property with some associated utility methods on Object is a nice way to get that. -- Christian _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedOn Jul 3, 2009, at 1:21 AM, Christian Plesner Hansen wrote:
function pointPlusNumber(a, b) { return new Point(a.x + b, a.y + b); } Function.defineOperator('+', Point, Number, pointPlusNumber); If you want to amend per Allen's suggestion by imposing fruitless, verbose ".prototype" taxes on the last line, so it reads Function.defineOperator('+', Point.prototype, Number.prototype, pointPlusNumber); (or with some other argument order), then what good have you done for users? Unless Point and Number are frozen or otherwise configured in a non-default way, the next line could change one or both .prototype values and make later instances miss the operator. Sure, that would be a case of "Doctor, it hurts when I do this" (so don't do it), but the same argument applies to instanceof, yet one writes (p instanceof Point), not (p instanceof Point.prototype). You can't have it both ways. Either mutable .prototype (or mutable function binding, e.g. Point in the global object, or whatever scope it's bound in) is a problem that requires freezing or equivalent lock-down, or it isn't. In any event, there's no reason to impose the .prototype tax, and instanceof-consistency (evolutionary :-P) reason not to.
Value types are new "UI". The benefits won't accrue to user-defined functions as constructors without more opt-in API. And value types deserve first-class syntax, just as classes as sugar do. C# has structs, as Sam pointed out. Without picking the exact syntax right now, the point remains that we have not been proposing only operators, or even only operators for user-defined constructor functions. We're talking about user-defined value types, ideally with concise, convenient defining as well as invoking syntax.
But adding operators without considering first-class value types (==/=== and literal syntax) looks like a mistake.
Foo.prototype = { bar: function () {...} ... }; But this is noisy and error-prone still, and you are wrong that what one "can live with" should be the last word. People "live with" all sorts of problems in the world, including in JS as it is, and certainly as it was before ES5 (which is definitely not the last word). Classes as sugar proposes to sweeten such "method definitions", to lighten the boilerplate tax and reduce opportunities for nesting/bracing/"this"-binding errors.
Perhaps just inconsistency for the sake of low-level explicitness, to be as fair as possible. Or possibly pedantry, but again: if user-defined constructor .prototype references are used and neither the constructor binding in its scope object nor the .prototype property are non-writable and non-configurable, then what is the point? If the "type" name and .prototype *are* locked down (as with classes as sugar), then there's no good reason for .prototype, it's just a "syn-"tax and an unjustified inconsistency with respect to instanceof.
function "+"(a :Point, b :Number) { return new Point(a.x + b, a.y + b); } function "+"(a :Number, b :Point) { return new Point(a + b.x, a + b.y); } function "+"(a :Point, b :Point) { return new Point(a.x + b.x, a.y + b.y); } I'm not sold on Dylan's \+ (backslashes are troublesome and overloaded), but like quoting, it is a clean extension to the grammar.
I chose to make most things mutable in Netscape 2 (even built-in constructor.prototype, if memory serves) because I had too little time to get the core language right, and I knew users would need to monkey-patch. That was then. As noted earlier in this thread, with ES5's Object.freeze etc., it will become harder over time to count on the ability to monkey-patch.
You still haven't addressed the ==/=== relation and other "value type" issues such as literal notation extensibility.
The internal property is invisible to users of the language, and could be replaced by other implementation techniques. That's why I question it as a spec device. You really did propose symmetric multimethods, but hooked them up "backstage of the language" using "this+" and "+this", etc. At this stage I contend we should focus on the fundamental design elements including multimethods, and not on the ES1-5-style internal property spec hacks. And of course prettier, more usable bikesheds are fair game, if anyone wants to comment. Operators and other value type extensibility are motivated by usability problems with functions and methods and numeric literals jammed into strings. /be _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisited> There are lots of subsets in the language already, this is a teaching and
> user preference outcome that we can't and shouldn't stop. Multiple paradigms > and well-ordered subsets are not a problem, they are a success condition. > But adding operators without considering first-class value types (==/=== and > literal syntax) looks like a mistake. This is the critical point. Do you want operator overloading to extend to classic objects (that is, instances of user-defined functions) or to be restricted to the values/classes/types subset? That's not as much a technical discussion but a question of design philosophy. I assumed that integration with the existing language was desirable. If not then the proposal is moot. > No, because for two numbers a and b, a == b <=> a === b. In fact for any two > values, (typeof a == typeof b && a == b) <=> a === b. If we want to support > usable numeric and similar "scalar" type extensions, we might want to preserve > this logic. (I forgot to respond to this from a previous message) What does "usable" mean? If operators did work for classic objects why not use them for scalars? There's === but I don't know why you would want it to be anything other than object identity. Is anybody relying on (typeof a == typeof b && a == b) <=> a === b? _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedOn Jul 3, 2009, at 5:29 AM, Christian Plesner Hansen wrote:
> This is the critical point. Do you want operator overloading to > extend to classic objects (that is, instances of user-defined > functions) or to be restricted to the values/classes/types subset? > That's not as much a technical discussion but a question of design > philosophy. I assumed that integration with the existing language was > desirable. If not then the proposal is moot. Operators are currently hardcoded in the language, but the specification could be recast using multimethods or double dispach and then extended to allow users to define operators on new "types" -- but what would be the nature of those types? Anything like a number (primitive, as you note the wrapping with Number is an old hack) does not act like a reference (object, typeof x == "object") type. It's a value type. This quest for extensible value types was how TC39 generalized the problem statement raised by decimal. > What does "usable" mean? If operators did work for classic objects > why not use them for scalars? "Usable" takes in operators for new value types, along with literal notation. These are aspirations, not obviously out of reach. Waldemar's original JS2/ES4 work from ~10 years ago had "units": http://www.mozilla.org/js/language/js20-2002-04/core/lexer.html#units Since number, boolean, and string are not classic objects the generalization from hardcoding does not proceed from "classic objects" to novel scalar types, rather from the non-null/void primitive types. > There's === but I don't know why you > would want it to be anything other than object identity. Is anybody > relying on (typeof a == typeof b && a == b) <=> a === b? Lots of code on the web happens to depend on this because some folks (Doug Crockford recomments this) use === while others use == but "usually" with same-typed operands. I think I've even seen some code mix == and === usage. /be _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisited> Operators are currently hardcoded in the language, but the specification
> could be recast using multimethods or double dispach and then extended to > allow users to define operators on new "types" -- but what would be the > nature of those types? > > Anything like a number (primitive, as you note the wrapping with Number is > an old hack) does not act like a reference (object, typeof x == "object") > type. It's a value type. > > This quest for extensible value types was how TC39 generalized the problem > statement raised by decimal. If TC39 has decided that value types are the way to go then I agree, operators have to be considered in that context. On the other hand if operator overloading did work for ordinary objects I would suggest reconsidering whether value types were the simplest way to implement use cases like decimal. I'm no fan of having numbers and booleans hardcoded but at least it's an inconsistency that JS shares with most other languages. >> What does "usable" mean? If operators did work for classic objects >> why not use them for scalars? > > "Usable" takes in operators for new value types, along with literal > notation. These are aspirations, not obviously out of reach. Waldemar's > original JS2/ES4 work from ~10 years ago had "units": > http://www.mozilla.org/js/language/js20-2002-04/core/lexer.html#units Custom literals is a nasty problem. To work with decimal literals, for instance, the compiler can't be allowed to interpret numeric constants; that has to be delegated to the decimal library. Value types would probably make some aspects of it easier (though you have to watch out if you want to avoid one-pass semantics) but as far as I can see only the simple aspects. > Since number, boolean, and string are not classic objects the generalization > from hardcoding does not proceed from "classic objects" to novel scalar > types, rather from the non-null/void primitive types. That's a matter of which angle you want to view it from. But I take the point that the same holds for my view. >> There's === but I don't know why you >> would want it to be anything other than object identity. Is anybody >> relying on (typeof a == typeof b && a == b) <=> a === b? > > Lots of code on the web happens to depend on this because some folks (Doug > Crockford recomments this) use === while others use == but "usually" with > same-typed operands. I think I've even seen some code mix == and === usage. I won't try to guess if breaking this relation will give problems in practice. If it's really one of the central reasons for having value types it would be worth looking into. -- Christian _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedOn Jul 5, 2009, at 7:52 AM, Christian Plesner Hansen wrote:
> Custom literals is a nasty problem. To work with decimal literals, > for instance, the compiler can't be allowed to interpret numeric > constants; that has to be delegated to the decimal library. To clarify, we wouldn't make 123 or 3.14 be extensible (at least not without some "use decimal" pragma, which seems very difficult to implement, since it would not affect lexical scope or other compile- time constructs, but rather have runtime effects). You'd have to write 1.1m or similar. /be _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedOn Jul 3, 2009, at 1:21 AM, Christian Plesner Hansen wrote: >> Likewise, for user-defined function foo, foo.prototype is writable >> -- but >> not so for built-in constructor functions, and not so for classes >> as sugar >> (more below). > > All JS code currently in existence is based on user-defined functions. > For me that is the only case worth considering. That might explain > the differences in our views. > > I'm not a language revolutionary, I prefer gradual evolution. > >> The original "classes as sugar" sketch Mark did to general >> agreement at Oslo >> included freezing the class object (constructor function) *and* >> bind the >> class name as a const (neither writable nor configurable in the >> binding >> object or frame). This point disturbs me. Making classes frozen solves no existing JS programmer problems, introduces new restrictions with no mind paid to the current (useful) patterns of WRT the prototype chain, and introduces the need for a const that only seems there to make some weird security use-cases work. And I say that with all sympathy to weird language features and the security concerns in question. Why should this stuff be the default? It's time to admit that built- ins in JS are weird and that as the weirdos, they deserve to work harder to make things happen to/for them, particularly since language implementer code size is incomparably cheap compared to script author code size. Questions of "integrity" here to my mind should be justified by why they'll be good for ALL code, not just abuse of built-ins, and then weighed against other possible solutions. Freezing classes seems premature to me. Regards _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedOn Mon, Jul 6, 2009 at 6:10 PM, Alex Russell<alex@...> wrote:
>>> The original "classes as sugar" sketch Mark did to general agreement at >>> Oslo >>> included freezing the class object (constructor function) *and* bind the >>> class name as a const (neither writable nor configurable in the binding >>> object or frame). > > This point disturbs me. Making classes frozen solves no existing JS > programmer problems, introduces new restrictions with no mind paid to the > current (useful) patterns of WRT the prototype chain, and introduces the > need for a const that only seems there to make some weird security use-cases > work. > [...] > Questions of "integrity" here to my mind should be justified by why they'll > be good for ALL code, not just abuse of built-ins, and then weighed against > other possible solutions. Freezing classes seems premature to me. For low integrity patterns (or "loosey goosey" as Allen sometimes says), JavaScript has always been and will remain a fine language. In ES5 for the first time, it is possible to engage in high integrity patterns; but it is not easy. We don't need syntactic sugar to provide yet another way to express what can already be expressed easily. It is when something valuable becomes possible, but can't be made easy without syntactic sugar -- like high integrity programming in ES5 -- that syntactic sugar may be justified. So no, it doesn't need to be justified for all code. It just needs to be justified by code that isn't well served by ES5 as it is. No one is proposing removing JavaScript's support for making low integrity programming easy. As for whether high integrity is only of interest to security weirdos, I'll just remind everyone that the notion of objects [Simula, Smalltalk, Actors] -- or similarly abstract data types [Clu] -- was conceived from the beginning as: reusable packages of encapsulated state together with the code responsible for maintaining that state's invariants and presenting an abstract interface. That interface that quickly came to be characterized in terms of pre-conditions and post-conditions [Liskov]. Virtually none of the systems or literature that brought about this revolution was concerned with security per se. These notions were created for the sake of modularity, abstraction, and flexible composability. None of these concerns have gone away; hence the frequent complaint that JavaScript today provides inadequate support for serious large scale programming -- a need which classes-as-sugar hopes to address. Finally, I make no apology for being a security weirdo or for wanting to see JavaScript become more securable. JavaScript is uniquely positioned as the fastest growing portion of many organizations' attack surface; and often also their weakest link. This is a role for which it was not designed and for which it has been ill suited. By the same token, JavaScript is also uniquely positioned to capitalize on prior work on programming language security. It's continued evolution should indeed be shaped by the unique niche it occupies. -- Cheers, --MarkM _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedOn Jul 6, 2009, at 6:10 PM, Alex Russell wrote:
If you're worried that the 'class' keyword will hypnotize the masses into locking things down overmuch, then you don't trust the masses very much. They'll mostly avoid new stuff, and any who get burned by inability to monkey-patch (which is *not* an unmixed blessing) will retreat, and probably start a "don't use 'class'" movement. This is not 'final' in Java (which pace Mark, I believe was overused, including in the standard library, but which also has different enough semantics that I wouldn't drag it in here as a precedent. We're talking about sugar for ES3 and ES5 facilities already implemented, or nearly implemented. There was no such underlying metaprogramming API prefiguring final in Java, AFAIK. There should be sugar for high- and low-integrity programmers, and then they can get back to their standoff. :-/ /be _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
|
|
Re: Operator overloading revisitedBrendan Eich wrote:
> On Jul 6, 2009, at 6:10 PM, Alex Russell wrote: > >> This point disturbs me. Making classes frozen solves no existing JS >> programmer problems, introduces new restrictions with no mind paid to >> the current (useful) patterns of WRT the prototype chain, and >> introduces the need for a const that only seems there to make some >> weird security use-cases work. And I say that with all sympathy to >> weird language features and the security concerns in question. >> >> Why should this stuff be the default? > > What Mark said, I agree completely with his post: "this stuff" is *not* > "the default" -- function as constructor is the default, and no one is > salting that syntax. So why are you against sugar for high-integrity > programmers? It's not as if those weirdos will take over the world, right? We security weirdos fully intend to take over the world. You have been warned. -- David-Sarah Hopwood ⚥ http://davidsarah.livejournal.com _______________________________________________ es-discuss mailing list es-discuss@... https://mail.mozilla.org/listinfo/es-discuss |
| < Prev | 1 - 2 - 3 | Next > |
| Free embeddable forum powered by Nabble | Forum Help |