Set constructor arguments

# Peter Michaux (12 years ago)

In the proposal, the Set constructor takes no arguments.

harmony:simple_maps_and_sets

Could it take a list of initial elements to be included in the set?

var s = new Set('alpha', 'beta'); s.has('alpha'); // true

I think this is a lot better than the verbosity that is

var s = new Set(); s.add('alpha'); s.add('beta');

Peter

# Oliver Hunt (12 years ago)

I saw a reference to it being modified to take an array(-like?) as a parameter. While I can see an argument in favour of a single array argument I can also see using a set of parameters that initially populate the set. We just shouldn't allow both (and introduce another version of the horror that is the Array constructor).

With the existence of the spread operator I think that there's a good argument in favour of the multiple parameters approach.

# Erik Arvidsson (12 years ago)

The default argument should probably just be an iterable.

# Michael A. Smith (12 years ago)

…But we wouldn't want to require the arguments to the set constructor to require instantiation itself/themselves, right? If a set has to be constructed with an Iterable, and cannot (also) be constructed with individual atoms, then you'll end up with use cases like Set(['foo', 'bar', 'baz']), which requires two constructions, one of which is essentially a waste. If allowing both forms of the constructor is distasteful, then why not just go with the multiple parameters, approach, and implement toSet() as a method on appropriate Iterables?

-Michael A. Smith

# Axel Rauschmayer (12 years ago)

My suspicion: All these features make sense for the full-blown collections API, but for sets I’m not sure at which point they wouldn’t be “simple”, any more.

Oliver Hunt makes a good case: “With the existence of the spread operator I think that there's a good argument in favour of the multiple parameters approach.”

An additional option is to introduce a “static” factory method: Set.fromIterable().

# Andrea Giammarchi (12 years ago)

Set.fromIterable() as factory is basically same as

var s = Set.apply(null, ['alpha', 'beta']);

since as it is for all native functions the constructor is a factory itself and no need to "new"

my 2 cents, br

# Axel Rauschmayer (12 years ago)

True. Assuming that the spread operator works on any iterable, it is also the same as new Set(...iterable)

# Andrea Giammarchi (12 years ago)

also ...

var s = new Set; ['alpha', 'beta'].forEach(s.add, s);

should do the trick, assuming the signature of "add()" accepts one argument only

# Andrea Giammarchi (12 years ago)

thinking about the add behavior, where no duplicated values will be added, this argument may cause some logic headache anyway

Set([1, 2, 1]) what should happen ?

Also I would probably never use typed arrays to Set procedure ... that sounds against performances

About new syntax, generators are new too so ...

var s = Set(...(x * x for x of primes));

as well as all others ... still, I start thinking Set constructor is good as it is

my 2 cents

# Peter Michaux (12 years ago)

On Tue, Feb 14, 2012 at 12:09 AM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

thinking about the add behavior, where no duplicated values will be added, this argument may cause some logic headache anyway

Set([1, 2, 1]) what should happen ?

I think that should be a set with one element. The element is an array of length three.

Peter

# Andrea Giammarchi (12 years ago)

if you accept a single argument, of course, but what if you Set(..[1, 2, 1]) then ?

magic add through Set constructor does not sound good to me

# Dean Landolt (12 years ago)

On Tue, Feb 14, 2012 at 3:07 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

if you accept a single argument, of course, but what if you Set(..[1, 2, 1]) then ?

Set(1, 2, 1) then? Are you suggesting this should throw? So you'd need to dedupe your arguments before you construct a set with them? Isn't that a primary use case of sets?

# Andrea Giammarchi (12 years ago)

nope, Set does not even accept arguments as it is now ... does it ?

# Mark S. Miller (12 years ago)

On Tue, Feb 14, 2012 at 12:23 PM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

nope, Set does not even accept arguments as it is now ... does it ?

Not now. But this thread suggests changing it to do so. I think I agree but don't yet have a strong opinion about whether Set should have a single iteratable parameter or a rest parameter of the individual elements.

Relevant question: What should spread (... in a call expression) do when its operand is an iterator or iteratable? Currently spread simple treats its operand as array-like, in which case I think perhaps Set should stick with a single parameter. If we can generalize spread to enumerate the values obtained from an iterator, then I think perhaps Set should go with the spread parameter.

Whatever we decide for Set should also guide what we do for Map and WeakMap of course.

# Brendan Eich (12 years ago)

+1 on ... (spread) exhausting an iterator to expand the iterated values into positional parameters or initialisers.

# Andrea Giammarchi (12 years ago)

As long as we don't end up with ambiguous constructor as Array could be ( Array(1) VS Array([1]) ) ... so what if the iterable is the value I would like to add() automatically ?

If it's about auto magic population with auto magic filtering and ordering of potential duplicated values ( and already too much magic, imo ) I would keep Set.length 0 accepting 0 to N arguments rather than a single one so yes, spread through iterable would be ideal.

On the other hand, Map and WeakMap have different relation, based on pairs, and different kind of pairs ( object only or anything )

What if we leave these constructors in peace and let outer wrappers eventually decide what kind of sorcery should be applied during initialization ? :-)

br

# Dean Landolt (12 years ago)

On Wed, Feb 15, 2012 at 3:56 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

As long as we don't end up with ambiguous constructor as Array could be ( Array(1) VS Array([1]) ) ... so what if the iterable is the value I would like to add() automatically ?

Don't use spread on it.

If it's about auto magic population with auto magic filtering and ordering of potential duplicated values ( and already too much magic, imo ) I would keep Set.length 0 accepting 0 to N arguments rather than a single one so yes, spread through iterable would be ideal.

On the other hand, Map and WeakMap have different relation, based on pairs, and different kind of pairs ( object only or anything )

What if we leave these constructors in peace and let outer wrappers eventually decide what kind of sorcery should be applied during initialization ? :-)

I think that's the intent of allowing spread to exhaust an iterator.

# Dean Landolt (12 years ago)

On Tue, Feb 14, 2012 at 11:49 PM, Brendan Eich <brendan at mozilla.org> wrote:

+1 on ... (spread) exhausting an iterator to expand the iterated values into positional parameters or initialisers.

What about infinite generators? Punt on any iterators without a predefined length? Otherwise wouldn't there be some kind of exception akin to a stack overflow?

# Mark S. Miller (12 years ago)

Many useful finite generators will not know their length ahead of time, so this would be a poor way to detect finiteness.

Infinite generators will always create many hazards of non-termination if provided to contexts expecting finite generators. Similarly looped data structures, if provided to contexts expecting acyclic data structures. For the latter, at least it can be detected (as JSON.stringify does).

While we could try to create nominal subtypes FiniteIterator and InfiniteIterator so that iterators can declare their alleged intent, IMO this is overengineering for a rare case. And provides no guarantees anyway. I think we should just live with the non-termination danger. We've been doing so for all other sources of non-termination since Turing ;).

# Allen Wirfs-Brock (12 years ago)

On Feb 15, 2012, at 7:48 AM, Dean Landolt wrote:

On Tue, Feb 14, 2012 at 11:49 PM, Brendan Eich <brendan at mozilla.org> wrote: +1 on ... (spread) exhausting an iterator to expand the iterated values into positional parameters or initialisers.

What about infinite generators? Punt on any iterators without a predefined length? Otherwise wouldn't there be some kind of exception akin to a stack overflow?

Or just an infinite loop.

Every time somebody invokes an abstracted operation there is the possibility that it will never complete.

We don't worry about the possibility that a stack overflow or infinite loop could occur in the code that provides the iterator so I don't see why we should worry about that in the code the exhausts the iterator.

# Tab Atkins Jr. (12 years ago)

On Wed, Feb 15, 2012 at 9:15 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Or just an infinite loop.

Every time somebody invokes an abstracted operation there is the possibility that it will never complete.

We don't worry about the possibility that a stack overflow or infinite loop could occur in the code that provides the iterator so I don't see why we should worry about that in the code the exhausts the iterator.

Yup. There's no real difference between:

let x = new Set(..someInfiniteIterator);

and:

let x = new Set((function(){while(1){}})());

So let's not worry about it.

# Dean Landolt (12 years ago)

On Wed, Feb 15, 2012 at 12:13 PM, Mark S. Miller <erights at google.com> wrote:

Many useful finite generators will not know their length ahead of time, so this would be a poor way to detect finiteness.

Yes but you can count the times you've iterated and punt when exceeding length, like a poor man's stack depth hint. But yeah, pretty hacky.

Infinite generators will always create many hazards of non-termination if

provided to contexts expecting finite generators. Similarly looped data structures, if provided to contexts expecting acyclic data structures. For the latter, at least it can be detected (as JSON.stringify does).

While we could try to create nominal subtypes FiniteIterator and InfiniteIterator so that iterators can declare their alleged intent, IMO this is overengineering for a rare case. And provides no guarantees anyway. I think we should just live with the non-termination danger. We've been doing so for all other sources of non-termination since Turing ;).

True. But this is a little different -- syntactically, spread doesn't exactly scream "I'm calling an arbitrary, potentially non-terminating function", but neither do getters. But I'm sure we'll survive.

# Jason Orendorff (12 years ago)

I wrote a script to look into how the set() constructor is used in Python. Take these numbers with a grain of salt, of course.

wiki.mozilla.org/User:Jorend/Python_set_usage

# Mark S. Miller (12 years ago)

it's awesome to see concrete data like this, even with all the salt grains. In TC39 discussions we often discuss questions like this, that should be informed by data, and proceed by intuition and guesswork instead, which is more like salt with a few grains of non-salt ;).

Seriously, I'm very glad to see this. I hope we all take it as a precedent. Thanks.

# Brendan Eich (12 years ago)

Dean Landolt wrote:

On Tue, Feb 14, 2012 at 11:49 PM, Brendan Eich <brendan at mozilla.org <mailto:brendan at mozilla.org>> wrote:

+1 on ... (spread) exhausting an iterator to expand the iterated
values into positional parameters or initialisers.

What about infinite generators? Punt on any iterators without a predefined length? Otherwise wouldn't there be some kind of exception akin to a stack overflow?

Slow script dialog, it's industry-standard.