On Nov 23, 2010, at 12:14 PM, P T Withington wrote: >> Harmony Proxies allow meta-programming of |in| already, via the |has| trap. >> So the answer to your quesiton "does the `in` operator also get overloaded?" >> is "Yes, but you have to write two traps, iterate and has". > > How does the `in` in for-in decide which of it's overloaded meanings applies? > Based on the type of the operand? Based on the existence of the enumerate > or iterate trap on the operand?
A proxy's handler is a single object with trap methods. These traps work together to create an abstraction. It should be a coherent abstraction (ideally, don't you think? ;-). If you're implementing an iterator, then the iterate and has traps are enough to make for-in and in agree. From Python experience, just iterate is enough. (JMatch is cute because it can synthesize both iteration and membership testing from Boolean formulae, but we're not going to try for anything like it.) If you're implementing a container, then questions of mutability arise. Immutable container proxies could have iterate, has, and get. Mutable containers might rather vend iterators via specific API methods, where the iterators are separate proxies that iterate over snapshots. There are lots of possibilities. > And despite the `in` in for-in either enumerating or iterating, the `in` > operator only has a single associated trap. The non-parallelism is bugging > me. There are at least two reasons for this: 1. Large and lazy objects cannot afford to enumerate all keys eagerly, as the enumerate trap wants. 2. "in" maps to the "has" trap, which must eagerly compute a boolean result. With just harmony:proxies as proposed, (1) motivates adding a derived trap, call it iterate, that can be used instead of enumerate. This is the proposed solution to the large/lazy object problem that we agreed to fix when moving Proxies to harmony:proposals status. Notice how "in" does not have a large/lazy problem. Membership queries must be answered eagerly, all at once, with a boolean result. This is (2). Another reason (3) is to support iterators as a special kind of proxy. All objects can be enumerated if not iterated, but not all objects are iterators. Adding a derived, lazy-iteration trap to use instead of enumerate allows proxies to implement iterators for arbitrary value streams. This is something we propose separately from proxies, but build on proxies (rather than reinventing obvious wheels). We thus handle large/lazy object enumeration (string typed key streams) as well as custom iterateion (arbitrarily typed value streams). The particulars matter. Tom invokes Einstein's famous "as simple as possible, but not simpler" dictum in his talks on proxies. Both JS's particulars, and hard realities to-do with scaling to large or lazily built objects, motivate non-parallel trap structure here. Trying to oversimplify will just make an unusably simplistic metaprogramming API that falls down on some use-cases, or too easily leads to incoherent abstractions. >>> Ramdom thought: Can I use destructuring in for-in? >>> >>> for ({key:value} in enumerable) >>> >>> for ([value] in iterable) >> >> Absolutely. Destructuring (separate proposal but composes well) applies to >> all LHS and binding forms. > > I was being too subtle. I was suggesting something like your JS 1.7 example, > where the 'top-level' destructuring is a pattern for the `in` operation. > `{key: value}` means I want the property keys and their values, No, that destructuring pattern {key: value} captures only the "key" property's value, bound to the name "value" on each turn of the loop. Making it a special form just breaks destructuring for no good reason. > `[value]` means I want the values, No, that means get the "0" property of the iterated value and bind it to the name "value". http://wiki.ecmascript.org/doku.php?id=harmony:destructuring > and `key` is (backward-compatible) shorthand for `{key: _}`. Destructuring > iteration over an hash of triples: > > for ({key: [s, v, o]} in tripledb) ... > > Too cute? Too restrictive and verbose, also does violence to destructuring by reinterpreting its syntax. Destructuring lets you pull properties from objects using object and array patterns modeled on initialisers, in a way that honestly desugars to property references and assignments. Iteration (whatever the syntax) lets you visit a sequence of values, whatever their type and deeper structure. Destructuring the iterated value conveniently binds values from the deeper structure to names or local variables declared in the iteration's loop syntax and used in the loop body. Thus there is no good reason to hardcode "key" as a name, or any particular one- or two-property pattern, in order to deal with objects as key/value stores using destructuring and iteration. We can leave destructuring unaltered and provide different iterators for the key, value, and key-value (and triple, quad, etc.) use cases. As dherman noted, iteration needs a meta-object protocol because neither 2 nor 200 iterator flavors are enough. This is a library issue, but right now libraries have no ability to extend iteration syntax of any kind, and instead must resort to closure-based functional iteration. Functional iteration certainly works, but it has its verbosity and closure-related runtime costs, and to many users familiar with nearby languages that have nice iteration syntax and metaprogrammability, the lack of those in JS cries out for usable syntax, which in the for-in form is rotting due to enumeration's messiness and underspecification. In the end, whatever we do with syntax (we agreed to add meta-programmable iteration at last week's meeting, details to be worked out over time), destructuring should compose without special cases. /be _______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss