ES4 draft: Vector

# Lars Hansen (18 years ago)

I enclose a slightly incomplete/rough draft for the Vector class. Please comment.

# Jon Zeppieri (18 years ago)

I just realized that my message from yesterday about the 'fixed' property (of the Vector class) was sent as a reply to the thread about the Map class... Sorry.

So, why a read/write, rather than a read-only, 'fixed' property?

-Jon

2008/3/3 Lars Hansen <lhansen at adobe.com>:

# Lars Hansen (18 years ago)

-----Original Message----- From: es4-discuss-bounces at mozilla.org [mailto:es4-discuss-bounces at mozilla.org] On Behalf Of Jon Zeppieri Sent: 5. mars 2008 13:04 To: es4-discuss at mozilla.org Subject: Re: ES4 draft: Vector

I just realized that my message from yesterday about the 'fixed' property (of the Vector class) was sent as a reply to the thread about the Map class... Sorry.

No wonder I was confused :-)

So, why a read/write, rather than a read-only, 'fixed' property?

It was my hunch when I designed that feature that code that uses fixed-length vectors wants to be able to catch errors most of the time, but also does not want to be stuck in the straightjacket that a settable-but-not-resettable fixedness property creates, leaving it unable to extend the vector in place after making it fixed.

# Jon Zeppieri (18 years ago)

On 3/5/08, Lars Hansen <lhansen at adobe.com> wrote:

So, why a read/write, rather than a read-only, 'fixed' property?

It was my hunch when I designed that feature that code that uses fixed-length vectors wants to be able to catch errors most of the time, but also does not want to be stuck in the straightjacket that a settable-but-not-resettable fixedness property creates, leaving it unable to extend the vector in place after making it fixed.

I don't think I've ever encountered this feature in another language/library. I've seen the following:

  • Languages like Java that have a distinction between fixed-length arrays and variable length ones

  • Dependently typed languages that allow you to express the type "Array with length N"

  • Languages that provide immutable arrays (which goes beyond the fixed-length guarantee) in addition to mutable ones

But I haven't seen vectors that can change from fixed- to variable-length and vice versa. So I guess I'm a bit skeptical of the demand for this feature. I'm trying to think of tasks where I need a vector to be a certain length for part of the computation and a different length for a different part -- and where I would also be upset to be forced to create a new vector (and copy into it) in order to accomplish that. (Of course, the "new vector + copy" is a simple invocation of the Vector constructor as a function.)

Obviously, we can save the cost of the copy if we have the 'fixed' flag, but we don't get the benefit of having a nice invariant.

Incidentally, since I brought up immutable arrays, above: intrinsic methods make it impossible to enforce immutability via subclassing, right? You could create an immutable sequence type through delegation, but not simply by overriding the mutators. Or is there a way to make the intrinsics unavailable publicly?

# Lars Hansen (18 years ago)

-----Original Message----- From: zeppieri at gmail.com [mailto:zeppieri at gmail.com] On Behalf Of Jon Zeppieri Sent: 5. mars 2008 15:52 To: Lars Hansen Cc: es4-discuss at mozilla.org Subject: Re: ES4 draft: Vector

On 3/5/08, Lars Hansen <lhansen at adobe.com> wrote:

So, why a read/write, rather than a read-only, 'fixed' property?

It was my hunch when I designed that feature that code that uses
fixed-length vectors wants to be able to catch errors most of the time, but also does not want to be stuck in the straightjacket that a settable-but-not-resettable fixedness property creates, leaving it unable to extend the vector in place after making it fixed.

I don't think I've ever encountered this feature in another language/library.

It takes an unsual mind to think of these things :-P

I've seen the following:

  • Languages like Java that have a distinction between fixed-length arrays and variable length ones

  • Dependently typed languages that allow you to express the type "Array with length N"

  • Languages that provide immutable arrays (which goes beyond the fixed-length guarantee) in addition to mutable ones

That's something we're missing but could have had.

But I haven't seen vectors that can change from fixed- to variable-length and vice versa. So I guess I'm a bit skeptical of the demand for this feature. I'm trying to think of tasks where I need a vector to be a certain length for part of the computation and a different length for a different part -- and where I would also be upset to be forced to create a new vector (and copy into it) in order to accomplish that. (Of course, the "new vector + copy" is a simple invocation of the Vector constructor as a function.)

Note, the Vector class called as a function does not create a new vector if its input is a vector. The prose is wrong in the draft, but the code is right.

There probably should be copy() method on the Vector class.

Obviously, we can save the cost of the copy if we have the 'fixed' flag, but we don't get the benefit of having a nice invariant.

What's the nice invariant?

Incidentally, since I brought up immutable arrays, above: intrinsic methods make it impossible to enforce immutability via subclassing, right? You could create an immutable sequence type through delegation, but not simply by overriding the mutators. Or is there a way to make the intrinsics unavailable publicly?

I don't understand the question. There is nothing magic about the intrinsic methods; if you override them all with versions that prohibit updating, then you should have an immutable array. The prototype methods delegate to the intrinsic methods on the "this" object.

I'm open to the possibility that the design with the "fixed" property is baroque and not worth the bother. (That it is unusual doesn't bother me.) I do feel it's important to be able to provide error checking for arrays; it's way too easy in ES3 to write out of bounds without getting any sort of error. It may be that a set-but-not-reset property is an alternative, or that the length must be provided to the constructor and that it is fixed after that. Both seem less flexible than the current design and I'm not sure what the benefits would really be (apart from safety against "hostile" code, but then you wouldn't be working in ECMAScript).

# Jon Zeppieri (18 years ago)

On 3/5/08, Lars Hansen <lhansen at adobe.com> wrote:

Note, the Vector class called as a function does not create a new vector if its input is a vector. The prose is wrong in the draft, but the code is right.

There probably should be copy() method on the Vector class.

It's identical to a slice() with no specified parameters.

Obviously, we can save the cost of the copy if we have the 'fixed' flag, but we don't get the benefit of having a nice invariant.

What's the nice invariant?

An actually fixed length. The proposed 'fixed' field is a debugging aid. A read-only version, on the other hand, is a guarantee that holds even if you hand your vector off to code you didn't write and don't trust.

Incidentally, since I brought up immutable arrays, above: intrinsic methods make it impossible to enforce immutability via subclassing, right? You could create an immutable sequence type through delegation, but not simply by overriding the mutators. Or is there a way to make the intrinsics unavailable publicly?

I don't understand the question. There is nothing magic about the intrinsic methods; if you override them all with versions that prohibit updating, then you should have an immutable array. The prototype methods delegate to the intrinsic methods on the "this" object.

Yeah, my mistake. For some reason, I was under the impression that intrinsic methods couldn't be overridden.

I'm open to the possibility that the design with the "fixed" property is baroque and not worth the bother. (That it is unusual doesn't bother me.) I do feel it's important to be able to provide error checking for arrays; it's way too easy in ES3 to write out of bounds without getting any sort of error.

Agreed.

It may be that a set-but-not-reset property is an alternative, or that the length must be provided to the constructor and that it is fixed after that. Both seem less flexible than the current design and I'm not sure what the benefits would really be (apart from safety against "hostile" code, but then you wouldn't be working in ECMAScript).

Mingling with others' code is something that already happens on the web; consider how (legitimate) services get around the cross-site restrictions browsers implement. I can't imagine security in ES is going to get less important.

# Brendan Eich (18 years ago)

On Mar 5, 2008, at 5:58 PM, Jon Zeppieri wrote:

On 3/5/08, Lars Hansen <lhansen at adobe.com> wrote:

Note, the Vector class called as a function does not create a new vector if its input is a vector. The prose is wrong in the draft, but the code is right.

There probably should be copy() method on the Vector class.

It's identical to a slice() with no specified parameters.

Thanks for pointing this out. Also equivalent to v.concat() for
vector v. True of all array-likes.

What's the nice invariant?

An actually fixed length. The proposed 'fixed' field is a debugging aid. A read-only version, on the other hand, is a guarantee that holds even if you hand your vector off to code you didn't write and don't trust.

What I have wanted in the past is a variable length vector whose
length I can freeze at some point. Akin to sealing an object
completely against mutation, after mutating it into good shape during
its early lifetime.

It may be that a set-but-not-reset property is an alternative, or that the length must be provided to the constructor and that it is fixed after that. Both seem less flexible than the current design and I'm not sure what the benefits would really be (apart from safety against "hostile" code, but then you wouldn't be working in ECMAScript).

Mingling with others' code is something that already happens on the web; consider how (legitimate) services get around the cross-site restrictions browsers implement. I can't imagine security in ES is going to get less important.

Lars's parenthetical aside I interpreted as trolling ;-).

I don't believe in a silver bullet for mashup security. On the other
hand, for programming-in-the-large sanity if not security, fixed
length vectors, fixtures in classes, nominal type relations based on
name not structure, namespaces and immutable name bindings -- all of
these integrity features help.

# Lars T Hansen (18 years ago)

On 3/6/08, Brendan Eich <brendan at mozilla.org> wrote:

What I have wanted in the past is a variable length vector whose length I can freeze at some point. Akin to sealing an object completely against mutation, after mutating it into good shape during its early lifetime.

Sounds like an argument for a standard meta::lock protocol in ES5 to handle both. ;-)

This reminds me (cc'ing Erik) that an alternative to overloading propertyIsEnumerable, which I still think is a good idea BTW because it solves a major problem with minimal fuss, is to put new methods on Object in a separate namespace. (Obviously we've done that a lot already.) The point here is that if we stick with meta:: as the desirable prefix for interacting with the representation, then meta::getAttributes and meta::setAttributes would make sense. We've discussed this in the past, but it seemed heavy-weight and possibly insecure and there were unclear use cases for the generality.

# Eylon Stroh (18 years ago)

<< FIXME Need to check a detail of the type system, namely whether Vector.<T> is a subtype of Vector.<U> if T is a subtype of U and U is

not *. >>

It shouldn't be. Consider Bar <: Foo, Baz <: Foo:

let baz:Vector.<Baz> = new Vector.<Baz>();

let foo:Vector.<Foo> = baz; // allowed if Vector.<Baz> is considered a

subtype of Vector.<Foo>

foo.add(new Bar()); let bazItem:Baz = baz.get(0); // type error!

Other comments:

push() is missing "length++;"

sort() is missing "return this;"

I think that the first for loop in unshift() should read for ( let i=1 ; i <= oldlimit ; i++ ) this[newlimit-i] = this[oldlimit-i]; also, "return newlength;" should be length = newlimit; return length; assuming that the length setter only sets the length property, without setting any default values (otherwise it would need to be moved up to before the for loops).

# Lars Hansen (18 years ago)

-----Original Message----- From: Eylon Stroh Sent: 7. mars 2008 12:22 To: Lars Hansen; es4-discuss at mozilla.org Subject: RE: ES4 draft: Vector

<< FIXME Need to check a detail of the type system, namely whether Vector.<T> is a subtype of Vector.<U> if T is a subtype of U and U is not *. >>

It shouldn't be. Consider Bar <: Foo, Baz <: Foo:

let baz:Vector.<Baz> = new Vector.<Baz>(); let foo:Vector.<Foo> = baz; // allowed if Vector.<Baz> <: Vector.<Foo>

foo.add(new Bar());

That would fail with a run-time check since Bar is not <: Baz.

Note that the type annotations here do not imply any absence of run-time checks. For this program, the annotations make no difference and could be left out. (Annotations only have a run-time effect in that they cause errors to be thrown on assigning to variables and properties if the types don't match, and they cause some primitive values to be converted to other primitive values.)

let bazItem:Baz = baz.get(0); // type error!

Wouldn't get that far.

Anyhow we've decided against covariant vector types except that Vector.<T> <: Vector.<*> for all T.

Other comments:

push() is missing "length++;"

It is not; writing to this[length] updates length.

sort() is missing "return this;"

Indeed.

I think that the first for loop in unshift() should read

for ( let i=1 ; i <= oldlimit ; i++ )
    this[newlimit-i] = this[oldlimit-i];

I think the test is i <= numitems but otherwise you're right, it needs to run from 1.

also, "return newlength;" should be length = newlimit; return length; assuming that the length setter only sets the length property, without setting any default values (otherwise it would need to be moved up to before the for loops).

Superficially this is the same issue as for push(), length has already been updated by the assignments in the first loop. So the explicit update is not needed before return. But the first loop is not actually correct, it needs to set the new length first otherwise the assignments will be illegal if more than one value is unshifted in.

Thanks for the careful reading.

# Waldemar Horwat (18 years ago)

new Vector.<T> ( length=..., fixed=... )

It would be helpful for readability to have the types here.

The |Vector| constructor is implementation-defined.

This is misleading. Usually when a standard states that something is implementation-defined, it means that its semantics are not specified in the standard. Having no standard-defined vector constructor would be strange indeed.

If you merely don't want to write pseudo-code for the Vector constructor, please just omit the "Implementation" section.

Vector( object )

When the |Vector| class object is called as a function, it creates a new variable-length |Vector| object of type |*| ...

static meta function invoke(object) { if (object is Vector.<*>) return object;

The description and code don't match.

[Also, I'm a bit unclear about how parametrized types currently work. The overview document has examples that seem to indicate that you call bound types as functions. What happens when you call Vector.<Foo> as a function and pass it a Vector.<Bar>?]

toLocaleString: This seems overspecified. Do you want to explicitly define what happens if the vector is modified in the middle of running this?

concat:

Vector<X> cannot be a subtype of Vector<Y> even if X is a subtype of Y. If it were, the type system would be unsound: you could pass a Vector<X> to a function F expecting a Vectory<Y> and then have F write a Y into that vector.

every, filter, etc.: These seem overspecified. For example, the definition of filter states that the implementation must perform the lookup and fetch of each found element twice, which unnecessarily forbids more efficient implementations.

forEach: Why is clamp here?

"The static indexOf method ...": indexOf isn't a static method.

Why does indexOf return AnyNumber?

lastIndexOf: What happens when you do i-- on a uint with the value of 0? Do you get 4294967295?

sort: Doesn't return anything.

splice:

if (items.length < delcnt) {
    let shift = delcnt - items.length;
    for ( let n=0, i=first; n < shift ; n++, i++ )
        this[i] = this[i+shift];
    length -= shift;
}
else {
    let shift = items.length - delcnt;
    for ( let n=shift-1, i=first+shift; n >= 0 ; n--, i-- )
        this[i] = this[i-shift];
}

Both of these seem wrong. I think there are at least four different errors here.

unshift:

Need to raise length first.

for ( let i=0 ; i < numitems ; i++ )
    this[newlimit-i] = this[oldlimit-i];

The bounds on this loop are wrong.

1.4: Typo: "defined on directly on"

When you iterate through Maps, you get the old length if the Map is modified during the iteration. When you iterate through Vectors, you get the new length. The discrepancy seems jarring.

fixed should be a constant property or be removed altogether. It's useless as a variable -- anybody can change the length of the vector at any time anyway, and now code that hands out references to vectors to clients has to deal with bozos who make those vectors fixed just for grins.

What happens if you try to index a vector with +Infinity or NaN? I assume it's RangeError, but don't know enough about how numerically named properties work.

prototype function every(this:Vector.<*>, checker, thisObj=undefined) (this.intrinsic::every(checker, thisObj is Object) ? thisObj : null);

prototype function filter(this:Vector.<*>, checker, thisObj=undefined) (this.intrinsic::filter(checker, thisObj is Object) ? thisObj : null);

prototype function forEach(this:Vector.<*>, eacher, thisObj=undefined) (this.intrinsic::forEach(checker, thisObj is Object) ? thisObj : null);

Why are you passing a boolean as the thisObj argument to intrinsic::every et al.?

Waldemar