Why does legacy content break when making array-likes real arrays?
On Sun, Dec 30, 2012 at 11:23 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:
I saw this happen, and was sad, but never understood what the legacy problem was.
bugs.webkit.org/show_bug.cgi?id=81573
In particular bugs.webkit.org/show_bug.cgi?id=81573#c45
The problem with doing this for HTMLCollection
is that HTMLCollection
supports named properties. (No idea why that happened.) We could maybe
have HTMLCollection.toArray()
or some such at some point if that's
found desirable. Haven't seen that many requests for such a thing yet.
Domenic Denicola wrote:
From: Anne van Kesteren
On Sun, Dec 30, 2012 at 10:22 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
- Array-like objects [not completely fixed (DOM...), but
arguments
We've made some effort towards changing some of this, but legacy content is against us. :/I saw this happen, and was sad, but never understood what the legacy problem was.
Similarly, when people ask me "why can't they just make arguments a real array" (perhaps with magic aliasing behavior in sloppy mode), I don't know the answer.
So just out of curiosity, how does legacy code break when making array-likes into real arrays? I feel like the Liskov Substitution Principle should have our back here.
LSP is not magic. Works better when you have a type system.
The history of what went wrong is recorded here:
mail.mozilla.org/pipermail/es5-discuss/2009-August/003112.html
Read the followups. I'll cite Oliver's message in full below.
Problem with Arguments inheriting from Array
Oliver Hunt oliver at apple.com /Mon Aug 17 16:08:21 PDT 2009/
I implement the logic to make the Arguments object inherit from Array in WebKit last friday and it's quickly turned up a severe incompatibility with Prototype.js, specifcally Prototype's isArray function:
isArray: function(object) {
return object != null && typeof object == "object" &&
'splice' in object && 'join' in object;
}
This breaks at the very least a number of Apple sites and Nasa.gov -- that's just two of the top row of sites onhttp://www.prototypejs.org/real-world , other sites didn't appear to be broken but then i was merely looking at loading the front page, nothing more.
Unfortunately the use of 'in' means we also can't just shadow a less useful function with null or undefined for now.
Due to these site breakages, caused by a major compatibility problem in a fairly major library it seems infeasible to attempt to retrofit array like behaviour onto arguments.
--Oliver
The problem was code that incorrectly assumed instanceof to mean that the
instance was an Array
in combination with Array.prototype.concat
.
There was a bug in Google's Closure Library which caused almost all Google's apps to fail due to this. The library was updated as soon as this was found but there might still be code out there that makes the same incorrect assumption in combination with concat.
I wanted to make another try under a runtime flag in Chromium but was blocked by Apple.
To clarify, my message was about making NodeList
have Array.prototype
in
its prototype chain.
The primary issue is that most array-likes aren't array-like when it comes
to mutability. Most exposed indexed objects don't allow changes to their
indices, which is decidedly un-array-like for ~quarter of array operations.
It would be useful for there to be a separate "Indexed" class/prototype
that contained the Array methods that don't mutate this
, which Arguments
and NodeLists and a bunch of other stuff could inherit from, which would
also prevent backward compat issues with testing for instanceof Array.
On Sun, Dec 30, 2012 at 6:05 PM, Brandon Benvie <brandon at brandonbenvie.com> wrote:
The primary issue is that most array-likes aren't array-like when it comes to mutability. Most exposed indexed objects don't allow changes to their indices, which is decidedly un-array-like for ~quarter of array operations.
The issue is not about the mutation methods even though it is non sensical to call push on an immutable array.
It would be useful for there to be a separate "Indexed" class/prototype that contained the Array methods that don't mutate
this
, which Arguments and NodeLists and a bunch of other stuff could inherit from, which would also prevent backward compat issues with testing for instanceof Array.
That would not help in the case with NodeList
. The fix there would be
to have concat not do a brand check on its arguments. Allen has
outlined the problematic brand checks at
strawman:es5_internal_nominal_typing
True, concat specifically needs to be changed. But it is the only Array method that does that check (the only other internal checks for brand BuiltinArray are in apply and isArray I think).
On Dec 31, 2012, at 10:39 AM, Brandon Benvie wrote:
True, concat specifically needs to be changed. But it is the only Array method that does that check (the only other internal checks for brand BuiltinArray are in apply and isArray I think).
In support of Array subclassing, I have a tweak to concat that allows properly formed subclasses of Array to use concat and produce subclass instances while still preserving the current behavior for existing us3w. I'll be ready to post the full details soon.
The work so far in the spec on enabling subclassing has been awesome and I look forward to seeing this!
From: Anne van Kesteren
I saw this happen, and was sad, but never understood what the legacy problem was.
Similarly, when people ask me "why can't they just make arguments a real array" (perhaps with magic aliasing behavior in sloppy mode), I don't know the answer.
So just out of curiosity, how does legacy code break when making array-likes into real arrays? I feel like the Liskov Substitution Principle should have our back here.