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
[email protected]
https://mail.mozilla.org/listinfo/es-discuss