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

Reply via email to