Infrequently Noted

Alex Russell on browsers, standards, and the process of progress.

Comments for Function-ality


Hey Christopher,

JS is a multi-paradigm language, but the essential paradigms -- no matter how you choose to write your code -- are that we have objects and functions. What WebIDL is asking of us is that we accept INVISIBLE constructors. That is, classes whose instances you come face-to-face with but can't call "new" on the way the system does. No matter how you think idiomatic JS should be written, that's just offensive. Now, maybe you can make the case that what we instead might want are joined factory/ctors like "Array" where you can use both "new Array()" and "Array()" and get new instances. Fine. But WebIDL doesn't give us that either. The fact reamains that these things act like functions under the covers but don't expose themselves that way to us. It's inconsistent and creates mental overhead.

And anyway, who says you get to keep the system from getting better for folks who want to program in a different style to the one you do ;-)

Regards

by alex at
Which is not to say -- and I've mentioned this before -- that I am against making as many interface objects constructable as makes sense. We should do that.
Hey David,

There's nothing that can't be done here, and changing NodeLists to be Arrays is absolutely on my TODO list...it's one of the reasons why I'm look for us to get the ".length" issue fixed in JS, and I think a recent Proxies evolution will get us there.

More the the point, WebIDL can just decree that these things be Array types. Yes, that's magical today (we don't have a way to explain it), but it's surely less crazy than the current behavior.

Anyhow, we have patches for this "new-ing" stuff that we're using in a build of Chrome, and it sure feels nice to work with. No JS library needed. The name "HTML" in all of the class names is a terrible thing to look at, but it beats passing opaque strings to a factory function.

Regards

by alex at
Alex, you say "By not allowing new and .call() WebIDL is giving JS semantics the bird ..." but that's not true. Web IDL does allow new-able call-able interface objects. What it doesn't do is require it.

I agree with the point that David makes above, and which Anne and others have been making, which is that there are some interface objects where having them constructable makes no sense -- or at least no sense if you want the result of the "new" to be an object that implements that interface and nothing more specific.

It's true that these interfaces were designed in a Java happy era, and maybe that's why these interfaces like Node (which are more like abstract classes, really) exist. Do JS programmers ever create hierarchies that have abstract classes, not intended to be constructed? Do they somehow check for this and throw (as Web IDL requires for non-[Constructor] annotated interfaces) or do they just let a half functional object be created with the right [[Prototype]]? Is the latter actually helpful behaviour for the author?

"The name “HTML” in all of the class names is a terrible thing to look at, but it beats passing opaque strings to a factory function."

I suggested new Element("div") on www-dom; seems more obvious to me than new HTMLDivElement, and especially for cases where the construct name is not just "HTML" . ucfirst($tagName) . "Element".

Alex,

I think you're underestimating the difficulties involved in what you advocate. Yes, the DOM is the largest and most important JavaScript library. But it was designed as a Java library with abstract types, a baroque type hierarchy and awkward factory methods. It is alien to JavaScript. But at least it is internally consistent. Adding constructors across the board will make it some kind of hybrid, but won't make it feel like a JavaScript library. Using Node as a constructor, for example, just doesn't make sense. And adding a NodeList() constructor isn't going to make nodelists feel native. For that, the entire DOM would need to change to use JS arrays.

I think it is worth pushing on the DOM spec to add more features that feel like native JS, including constructors that make sense. And maybe it is worth advocating for a grand redesign of WebIDL so it becomes more natural for future spec authors to create APIs that feel like JS rather than C++. But I don't think you can get natural JS APIs by just tweaking WebIDL around the edges.

The Node.append() stuff being debated for DOM4 seems promising. Perhaps someone (you?) can create a JS library to implement that new method and at the same time include the constructors you propose so we can all try them out in practice.

Alex,

Let me preface this by saying that your I/O talk ("Learning to Love JavaScript") is the reason I'm working in JS right now.

But. However.

Crockford would argue (and I agree) that we largely do not need 'new' or 'call()'. We do sometimes need 'apply()'. I posit that while there is nothing wrong with 'new' and 'call()' --JS implements them perfectly well, their use causes us to think of JS in more classical terms. And this is potentially bad.

JS is a functional language and begs to be used in functional ways. We need neither 'new' nor 'call()' to accomplish most of our work, so why would their omission be a problem?

Great last line. I love the state of mind thinking about the dom as a JS library.
Cameron:

I think you're still not understanding. You're saying "we allow what is natural", the also true inverse of which is "we allow what isn't natural".

JS, on the other hand, doesn't allow what isn't natural.

Let me repeat myself: if you have a function in JS, you can use new against it. End of story.

The fact that WebIDL breaks this invariant is the bug. Arguing about the utility of it is really strange. WebIDL should be accepting JS as it is and building an API for the DOM in it, not projecting what's "right" for C++ or Java into JS. Do you disagree?

by alex at