Harmony Of My Dreams

Continuing in the vein of paren-free, I’d like to present a refreshed vision of JavaScript Harmony. This impressionist exercise is of course not canonical (not yet), but it’s not some random, creepy fanfic either. Something like this could actually happen, likelier and better if done with your help (more on how at the end).

I’m blurring the boundaries between Ecma TC39’s current consensus-Harmony, straw proposals for Harmony that some on TC39 favor, and my ideas. On purpose, because I think JS needs some new conceptual integrity. It does not need play-it-safe design-by-committee, either of the “let’s union all proposals” kind (which won’t fly on TC39), or a blind “let’s intersect proposals and if the empty set remains, so be it” approach (which also won’t fly, but it’s the likelier bad outcome).

Anyway, it’s my blog, and my current dream. I hope you like it. Talk and dream back at me, and with any luck we’ll build a better Harmony-in-reality.

little languages

Calling JavaScript a little language is polite but false at this late date: ES5.1 weighs in at over 100,000 words, with hundreds of nonterminals in the lexical and syntactic grammars.

I would say that the same goes for CoffeeScript, although I get the point in its use of the phrase “little language”: concise expression-language sugar for the lever-arm, using JS-in-full as implemented in all browsers as the fulcrum, for maximum productivity leverage. CoffeeScript is well done and more convenient to use than JS, provided you buy into the Python-esque significant space and the costs of generating JS from another source language. But semantically it’s still JS.

Could JS evolve to be a better “little language” in both surface and substance? Implementors and users will impose some fuzzy but obvious and (past the fuzz) hard limits. JS can’t evolve directly into something too different. For instance, I believe JS implementors on TC39 would reject significant space instead of curly braces, or a mandatory bottom-up parser with disambiguation magic.

Meanwhile, polyfills such as CoffeeScript (when not run as a server-side code generator) may become more widely used, pushing JS in different directions. Still, it will be hard for polyfills to beat native code implementation, <script> tag prefetching, and the other built-into-every-browser advantages of pure JS.

Whatever happens with polyfills, JS’s fitful progress over its life so far suggests that it can evolve further, and significantly. Such evolution requires growth in the short run, to solve the obvious web-imposed problem of backward compatibility.

growing a language

Maturing languages grow even without web-wide compatibility constraints, and JS is no exception. We should continue to grow the language, keeping support for old forms while adding new forms to help users themselves grow the language.

That last link is to a video of Guy Steele’s famous talk. Here’s a cleaned-up transcript. One quote:

If we add hundreds of new things to the Java programming language, we will have
a huge language, but it will take a long time to get there. But if we add just a few
things—generic types, overloaded operators, and user defined types of light weight, for
use as numbers and small vectors and such—that are designed to let users make and add
things for their own use, I think we can go a long way, and much faster. We need to put
tools for language growth in the hands of the users.

Look past the Java specifics. This applies deeply to JS as well. Empowering users to grow the language is why modules, proxies, binary data, and even an operators/literals/value-types dark horse, are high priorities for Harmony in my view. Which is not to say we shouldn’t add anything else. Because:

I hope that we can, in this way or some other way, design a programming language
where we don’t seem to spend most of our time talking and writing in words of just one
syllable.

You can do anything with function in JS, but you shouldn’t have to — it over-taxes JS programmers and VM implementors to learn and optimize all the idiomatic patterns. Too much like writing with only one-syllable words.

grow to shrink

If we do this right, Harmony’s kernel semantics do not grow inordinately in complexity. Then users merely have to choose to use the simpler new syntax over the old, and for those users (and possibly for everyone, many years hence — sooner, if you use a translator to “lower” Harmony to JS-as-it-is), JS is in fact more usable and smaller in its critical dimensions.

Beyond users choosing to code in a subset, we could potentially shrink — or not grow, or grow less — the opt-in Harmony language by excluding misfeatures of “classic JS”. This was one of the ideas developed in paren-free and some followup comments: no messy, underspecified, not-quite-interoperable for (i in o) loop, only for i in o loops, comprehensions, and generator expressions, to take one example. ES5 strict mode already removes with. Harmony already proposes to remove the global object as top-most scope, to pick a non-syntactic example.

(Opt-in is required for Harmony because of new syntax. Yet developer brain-print conservation, existing code migration, shared-object-heap interoperation, and browser engine code re-use, all favor keeping Harmony “close” to JS-as-it-is. How close is the question. I’m in favor of pushing this envelope given the inertia of the standards setting and the conservatism of committees. Bear with me if you disagree.)

On the web, the only way to shrink is to grow first. As I put it at jsconf.us last year, provide better carrots to lead the horses away from the rotting vegetables, which can be cleaned up later.

finding harmony

Some of what’s below is already harmonious according to TC39. Some is new, some is not yet proposed. The idea is to give an overview of Harmony that covers all of the high points and adds some new spice, instead of referring true believers to the sprawling wiki and then hoping they can figure things out from recent changes and the discussion list.

Unlike the CoffeeScript docs, I’ll show JS as it is implemented today on the left, and Harmony-of-my-dreams code on the right. This emphasizes how we’re working to fill gaps in the language’s semantics, not simply add sugar that lowers from new syntax to old. There’s nothing wrong with desugaring, and I believe CoffeeScript and other front ends for JS have a bright future, but TC39 is charged with evolving the core language, especially in ways that can’t be done efficiently or at all in today’s JS.

With this context in mind, let’s dive in.

binding and scope

Block scoped let and const, not weird old hoisted (to top of function or script) var. Lexical scope all the way up, no global object on the scope chain. Free variables are early errors.

var still_hoisted = "alas"
var PRETEND_CONST = 3.14
function later(f, t, type) {
  setTimeout(f, t, typo) // oops...
}
let block_scoped = "yay!"
const REALLY = "srsly"
function later(f, t, type) {
  setTimeout(f, t, typo) // EARLY ERROR
}

Removing the global window object from the scope chain doesn’t mean it won’t be available, though; see modules below.

functions

[Presented in the spirit of the Mozilla Apologator:] I’m sorry for picking so long a keyword. Beyond the length of function, and for all the many wins of closures, the objects that result from evaluating function declarations and expressions have some very shaggy hair. Time for a trim, starting with syntax proposed by @Arv and @Alex, extended to work with binding keywords:

function add(a, b) { return a + b }
(function(x) { return x * x })
const #add(a, b) { a + b }
#(x) { x * x }

The # character is one of few ASCII punctuators available. My straw polls on better characters to use has not led to a clear winner, and this one is proposed for Harmony. CoffeeScript’s -> and => seem to require bottom-up parsing, so they’re not going to fly among implementors.

Beyond syntax, notice how the braced body can end in an expression statement that evaluates to the implicit return value. And here’s another difference from functions: #-functions are immutable and joined.

What to call these # functions? Ruby has given up hash rockets. Can JS coin a new hash-phrase: hash-funcs? Suggestions welcome.

We should not call these things lambdas, as that drags in untenable Tennent’s Correspondence Principle strangeness, such as return from a lambda returning from its enclosing function (if still active; otherwise you would get a runtime error).

tail position

The implicit return value does more than save six characters plus one space (relieving you of having to type return ), it also makes the last expression statement be in tail position. This contrasts with a JS function, which has an implicit return undefined; at the end of its body.

function cps(x) { not_tail(x) }
function cps_harder(x) {
  work(x)
  return tail(x)
}
 
const #cps_smarter(x) {
  work(x)
  tail()
}

At his JSConf.eu talk last fall, @Crock promoted the idea of proper tail calls, something we have wrestled with in TC39 since ES4 days. Tail calls are a feature of Scheme, providing an asymptotic space guarantee (in plain English, you can tail-call without growing the call stack inevitably to entrain the space for all args and vars active along the dynamic call chain).

I agree with Doug that tail calls would be a win, especially with evented code. The # function syntax allows us to give tail calls a boost and save you seven (14 total: function + return_#) keystrokes.

Some have objected that this creates an unintended completion value leak-hazard, but (a) we can improve the Harmony definition of completion value in #-functions, (b) the void operator I added for javascript: URLs back in ’95 stands ready, and (c) when in doubt, use function syntax or write an explicit return.

no arguments

The arguments object is another clump of hair to trim from functions in adding hash-funcs. With Harmony, we have rest parameters, so we don’t need no steenking arguments!

(function () {
  return typeof arguments
})() == "undefined"
#() {
  typeof arguments
}() == "undefined"

This haircut makes life simpler for web developers; it means even more to JS implementors.

lexical this

Another Tennent’s Correspondence Principle casualty: this default binding. When you call o.m() in JS, unless m is a bound method, this must bind to o. But for all functions in ES5 strict mode, and therefore in Harmony (based on ES5 strict), this is bound to undefined when the function is called by its lexical name (f, not o.m for o.m=f).

Binding this to undefined censors the global object, a capability leak. Apart from that fix, though, passing undefined as this is nearly useless.

Why not bind this to the same value as in the code enclosing the hash-func? Doing so will greatly reduce the need for var self=this or Function.prototype.bind, especially with the Array extras.

function writeNodes() {
  var self = this
  this.nodes.forEach(function(node) {
    self.write(node);
  }
}
function writeNodes() {
 
  this.nodes.forEach(#(node) {
    this.write(node);
  }
}

It’s great that ES5 added bind as a standard method, but why should you have to call it all over the place? If Harmony does not address this issue, I will count that a failure.

records

A hot-button issue with ES5: Object.freeze. Whose side are you on, Batman’s or Mr. Freeze’s? Simplistic to say the least, since even in one’s own small-world codebase, making some things immutable protects against mistakes. Never mind single- and multi-threaded data sharing and other upside.

However, calling an Object method around every object initialiser you want frozen is a drag, and even then, the JS engine has to stand on its head and spin around to figure out that all the many evaluations of such an expression could be shared (but for the violation of object identity, detectable via === and !==, that sharing would create; but that could be optimized too, at some further expense).

With # we can do better:

var point = {x: 10, y: 20}
point.equals({x: 10, y: 20})
const point = #{x: 10, y: 20}
point === #{x: 10, y: 20}

Not only does the # sigil allow us to create records that are hash-cons‘ed so there is only one object identity per nearest containing relevant closure; we also get == and != (and the triple-equals forms) for free. Object content-based equality.

tuples

You may have noticed a trend here. Gaps in the JS language in usability, semantic unity of purpose, and reliability or invariance, can be made up for by adding “hash forms” that are shorter or still short enough, better for optimization, and free from mutation hazards and other historic hair. The same goes for Arrays, via tuples:

var tuple = [1, 2, 3]
tuple[tuple.length-1] === 3
Array.prototype.compare = /*...*/
tuple.slice(0, 2).compare([1, 2]) == 0
tuple.compare([1, 2, 4]) < 0
const tuple = #[1, 2, 3]
tuple[-1] === 3
 
tuple[0:2] === #[1, 2]
tuple < #[1, 2, 4]

Not only are the equality operators (strict and loose) based on contents and not object identity (which is not material due to the implicit freezing of these array-like objects), tuples support relational operators (< <= > >=), again based on contents not identity.

Relationals could work on records too, using enumeration order (assuming we standardize that order sanely).

Negative indexing, something unlikely to be grafted onto Array in Harmony (see “shared-object-heap interoperation” point above), is more than a minor convenience in my book. It’s also something Harmony Proxy handlers can implement. So tuples should support negative indexing even if arrays do not. And it ought to be cheap to create a tuple from an Array instance.

If negative indexing works, can slices and ranges be far behind? I’ll leave those for another post.

Array.prototype is full of generic methods, most of which do not mutate this. It’s tempting to want tuples to delegate to Array.prototype, with optimizations possible (as fast engines do today for dense-enough arrays). I’ll throw this idea out and confess I haven’t thought through every corner of it.

One known bug in the Array.prototype methods that construct a new array object, e.g. slice: they always make an Array instance, instead of calling new this.constructor. I agree with @Alex that we ought to fix this bug in Harmony.

statements

Here I recap paren-free, which I have prototyped in Narcissus (invoked via njs --paren-free), but with an obvious and convenient extension:

if (x > y) alert("brace-free")
if (x > z) return "paren-full"
if (x > y) f() else if (x > z) g()
if x > y { alert("paren-free") }
if x > z return "brace-free"
if x > y { f() } else if x > z { g() }

We do not want else clauses to be braced no matter what. In particular, an if statement as the else clause should not be braced, to avoid rightward indentation drift. Therefore any statement that starts with a keyword need not be braced. This is a boon for short break, continue, return, and throw statements often controlled by if heads that guard uncommon conditions.

modules

The simple modules proposal, along with its module loaders adjunct, is the likely Harmony module system solution. Note that there is no left-hand side example written in current JS below — you’d need a preprocessor, not part of the language.

 
 
 
 
 
module M {
  module N = "https://N.com/N.js"
  export const K = N.K
  export #add(x, y) { x + y }
}

Modules are being prototyped in Narcissus by @little_calculist right now. More on this at the end.

iteration

The impetus for paren-free was the poor old for-in loop. I propose we break it utterly by requiring an unparenthesized head sporting implicit let binding, and the “always use the one true iteration protocol” semantics. Also, generators based on JS1.7, based on Python. All of this is pretty much as in Python, but built on proxies.

 
 
for (var k in o) append(o[k])
 
 
 
 
 
 
module Iter = "@std:Iteration"
import Iter.{keys,values,items,range}
for k in keys(o) { append(o[k]) }
for v in values(o) { append(v) }
for [k,v] in items(o) { append(k, v) }
for x in o { append(x) }
#sqgen(n) { for i in range(n) yield i*i }
return [i * i for i in range(n)]
return (i * i for i in range(n))

Migrating for-in loops into Harmony will require saying what you mean.

That "@std:Iteration"module resource locator is something I made up. It’s an “anti-URL” since it starts with @. The idea is to be able to name built-in modules without having to write URLs, and without colliding with any possible URL. You could imagine "@dom" too.

rest parameters

Instead of the bad-smelling arguments object, Harmony boasts parameter default values (not shown here) and rest parameters.

function printf(format) {
  var args = Array.prototype.slice
                  .call(arguments,1)
 
  /* use args as a real array here */
}
function printf(format, ...args) {
  /* use args as a real array here */
}
 
 
 

With hash-funcs, default parameter values and rest parameters are all you get — no more arguments.

Should tuples become harmonious, the question arises: how does a rest parameter reflect, as an array or as a tuple? The answer may depend on how important it is to splice, reverse, or sort a rest parameter. VM implementors would love the frozen tuple answer. Most JS hackers who cared would, I suspect, favor array over tuple here.

spread

A companion to rest parameters, the spread syntax allows one to expand an array’s elements as positional parameters or array initialiser elements. Finally you can write a generic constructor-invoking helper without using switch and eval:

function construct(f, a) {
  switch (a.length) {
    case 0: return new f
    case 1: return new f(a[0])
    case 2: return new f(a[0], a[1])
    default:
      var s = "new f("
      for (var i = 0; i < a.length; i++)
       s += "a[" + i + "],"
     s = s.slice(0, -1) + ")"
     return eval(s)
  }
}
function construct(f, a) {
  return new f(...a)
}
 
 
 
 
 
 
 
 
 
 

Even without the 0, 1, and 2 special cases, this significant savings in lines screams "semantic gap being filled!"

UPDATE: @markm emailed to remind me that ES5 fills the gap part-way, at the price of a bound function:

function construct(f, a) {
  var ctor = Function.prototype.bind
             .apply(f, [null].concat(a))
  return new ctor();
}
function construct(f, a) {
  return new f(...a)
}
 
 

ES5 helps, but I think it is time to prototype Harmony's spread for Firefox.next.

destructuring

Often in JS you'll find yourself unpacking the properties of an object into same-named variables. Destructuring binding and assignment (prototyped since 2006 in JS1.7 in Firefox) fill this gap:

var first = sequence[0],
    second = sequence[1]
var name = person.name,
    address = person.address
    // no easy misc solution
 
let [first, second] = sequence
 
const {name, address, ...misc} = person
 
 

The destructuring patterns mimic object and array initialisers, and raise the possibility of refutable matching in JS.

library missing links

Array.create, Function.create (like Function but with a leading name parameter), binary data, proxies, and weak maps.

Array.create(proto, [1, 2, 3]) // see also Array.createConstructor

Function.create(name, ...params, body);

const Point2D = new StructType({ x: uint32, y: uint32 });
const Color = new StructType({ r: uint8, g: uint8, b: uint8 });
const Pixel = new StructType({ point: Point2D, color: Color });
const Triangle = new ArrayType(Pixel, 3);

Proxy.create(handler, proto)
Proxy.createFunction(handler, call, construct)

const map = WeakMap()
map.set(obj, value)

These are just the big dogs in the Harmony standard library kennel, but worth some attention.

In particular, proxies really want weak maps. Weak maps are something JS has needed for ages in general. Have you ever kept objects in an array and searched by object identity, or else mutated objects to assign hashcodes to them? No more.

Binary data looks insanely useful, and we hope it will supplant WebGL typed arrays in due course.

closing

This is a long post. If you made it this far and take away anything, I hope it is Guy's "Growing a Language" meta-point. JS will be around for a very long time, and it has a chance to evolve until its users can replace TC39 as stewards of its growth. The promised land would be macros, for syntactic abstraction and extensibility. I am not holding my breath, but even without macros, the Harmony-of-my-dreams sketched here would be enough for me.

We aim to do more than dream. Narcissus is coming along nicely since it moved to github and got a shot in the arm from our excellent Mozilla Research interns last summer. We are prototyping Harmony in Narcissus (invoked via njs -H), so you can run it as an alternate <script> engine via the Zaphod Firefox add-on.

@andreasgal has a JS code generator for Narcissus in the works, which promises huge speedups compared to the old metacircular interpreter I wrote for fun in 2004. With good performance, we can actually do some usability studies of Harmony proposals, and avoid Harmony-of-our-nightmares: untested, hard-to-use committee designs.

A code-generating Narcissus has other advantages than performance. Migrating code into Harmony, what with the removal of the global object as top scope (never mind the other changes I'm proposing -- here's another one: let's fix typeof), needs automated checking and even code rewriting. DoctorJS uses a static analysis built on top of Narcissus, which could be used to find flaws, not just migration changes. Self-hosted parsing, JS-to-JS code generation, and powerful static analysis come together to make a pretty handy Harmonizer tool. So we're going to build that, too.

More on Narcissus and Zaphod as they develop. When the time is right, we will need users -- lots of them. As always, your comments are welcome.

/be

79 Replies to “Harmony Of My Dreams”

  1. Thanks for writing up these notes — most of these dreams would be incredibly useful. Speaking for my part, having a lexically-scoped “this” would be perhaps the second-most important change you could possibly make for improving JS usability. (The most important being: everything-is-an-expression).

    One bit that I don’t quite follow is “=>” and “->” necessitating bottom-up parsing. Considering that they’re not a valid sequence of operators in JS, can’t you just lex them as atomic tokens?

  2. @Weston: keyword parameters had their oxygen sucked up by passing object initialisers, for better or worse. No one has written up a proposal. Destructuring formal parameters in Harmony (also JS1.7 and up in Firefox) complement passing an object literal, further undermining the case for keyword params.

    @Jeremy: JS copied C’s comma operator all those years ago, so (a, b) -> a*b starts off like a parenthesized comma expression. If you add destructuring formal parameter patterns: ({a:x, b, …r}, c) -> x*b+c-r[0], it looks like a parenthesized object initialiser (r is being spread), possibly part of a larger comma expression.

    To decide late in a top-down parser (when you finall get to the ->) that the input actually was part of a function requires some bottom-up simulation, at least AST checking and relabeling. A top-down parser would have parsed the parameter list as a primary expression, and the contents inside the parentheses as a comma or other expression, including expression forms not legal as parameters.

    Destructuring assignment expressions already require a bit of this kind of top-down cover-grammar parsing, followed by semantic AST crawling to check for errors and relabel nodes. So I wrote “seem to require” about Coffee’s -> and => because it might be that JS with all the Harmony proposals including destructuring could still cope with infix -> instead of prefix-#. Still a bit of a stretch when you add destructuring params.

    /be

  3. I believe an integrated querying capability would be a big win over the long term as well. We already have in usage CSS queries, the shards of E4X, XPath, and complex code for DOM and Object tree searching. Now there is Crockford’s suggestion of wanting to add JSON path… In a couple years I wouldn’t be surprised if there aren’t 1 or 2 more DSLs becoming mainstream.

    If operators can become first class functions along with some reformed E4X/LINQ style methods/operators (returning generators perhaps which implement a generator form of filter(), sort(), etc). Then this could go a long way.

  4. I would have loved to see something about inheritance of functions, regarding conceptual integrity. Things like mixin’s for subconstructors would be unnecessary then. User’s could create custom function types (Constructor, Interface etc).

    Tim

  5. Looks good, things like rest parameters definitely look promising.

    Your “library missing links” makes me wonder if having rest arguments in the middle would be usable, as in #create(name, …params, body) {}. Probably hard to figure out what to actually pass though when called as create(0).

    Given the lack of a global, does that make it impossible to replace global functions? I’ve known people (mostly extending others’ code) that did things like f = oldFunc; oldFunc = function() { /* new stuff */; return f.apply(this, Array.slice(arguments)); }.

    That same set of people would also dislike Object.freeze and anything else that prevents them from doing other Magical Hack style tricks. Yes, it’s ugly, but I feel ugly for the uncommon case beats impossible any day 😉

    The Iter things don’t look very object-oriented (compared to, say, all the methods on Array and Array.prototype), though I guess that’s inevitable (and they properly live on the module). Let’s hope it doesn’t cause confusion with the in-Firefox Iterator function.

  6. Brendan, this is a really nice dream! I would love to see this implemented.

    However, I must object to the implicit return. Yes, it saves you some typing. But my experience with this feature in Perl was pretty negative. For one, the return keyword allows you to scan a lengthy function and find the return value easily (particularly with syntax highlighting). It also happens that I forget a return statement. And then I would like to get a warning (“function doesn’t always return a value”) or an error because the return value is undefined. Having the application continue working with a random value is a debugging nightmare.

    I hope that the destructuring assignment will allow for the following use case:

    let [key, value] = line.split(“=”, 2);

    And with simply assign undefined to value if the line is something like “foo”. This scenario used to produce a warning in Gecko and forced me to do the validity checks first – at which point using the destructuring assignment became pointless.

    And finally a question: does “no global object” mean that variables cannot be accessed by name? This is something that I need to do occasionally, e.g. to replace a bunch of global functions by their wrappers (in a loop). Having to use eval() for this kind of thing would be very ugly. So while I agree that not putting all the global variables into the window object is great, this is still a scenario that IMHO needs to be considered.

  7. @Tim: I couldn’t fit them in, and there is a distinct lack of Harmony on some crucial points, but TC39 has several irons in that fire. One is traits:

    https://wiki.ecmascript.org/doku.php?id=strawman:syntax_for_efficient_traits
    https://wiki.ecmascript.org/doku.php?id=strawman:traits_semantics

    but some don’t like the freeze usage. Another is object initialiser extensions:

    https://wiki.ecmascript.org/doku.php?id=strawman:object_initialiser_extensions
    https://wiki.ecmascript.org/doku.php?id=strawman:obj_initialiser_constructors

    but these aren’t quite ripe for picking, yet.

    The traits work clearly addresses mixins, with error checking but also as noted, and the checking depends on this, freezing. It may be that a less frosty approach could work. The syntax is a bit heavy, too (read down to find the first example, which a later example re-renders more concisely).

    Jury definitely still out, and tl;dr as it is, so I’m making up for this cut in the comments.

    /be

  8. @Mook: that Function.create(name, …params, body) was an example call, so the …params is a spread special form. I didn’t want to write out param1, param2, etc. or use a meta-ellipsis that might be mistaken for concrete syntax. Spread and rest are mirror twins, though, so you could write a Function.create function definition (not call expression) in Harmony with formal parameters declared in exactly the same way. Cool!

    /be

  9. @Mook: replacing globals can be done by getting your hands on the global object and getting and setting properties. Lexical references (f, f = …) won’t do it, but (g = DOM.window; g.f, g.f = …) will. Nothing is impossible, just different for a reason: to get rid of the capability leak (crock’s big security hole, the global object as scope), and to make collisions easier to control with module declarations and imports. Oh, and this all optimizes a lot better and easier, to boot.

    Iter as a sub-module “@std:Iteration” of the standard core language module is not meant to be O-O as in classical OOP (but neither is most of JS :-P). Rather, it is based on Python’s iteration protocol, with a couple of cleanups around generators. It’s closer to structural typing than OOP. There is no Java-like Iterator class.

    BTW, Iter was just the name declared in that example; I could have used Iteration or I or foo (module users name MRLs but declare the module’s lexical const name for themselves, within the parent lexical scope they control).

    The Array generic methods really want to be free functions that can be mixed into various implementations and have their leading |this| parameters curried, for use with arbitrary objects that satisfy a notion of an array-like structural type or “contract”. These are far from a classical-OOP pattern.

    /be

  10. @Wladimir: implicit return has foes as well as fans (IIRC Joe Hewitt likes to mix return e; and falling off the end of a JS function). This might cast it out of Harmony if the committee plays it too safe in the face of controversy. I hope that JS is big enough and multi-paradigm enough for us all to get along, with good old function for foes of (unwarned-about) implicit return, and # for fans of (warning-free) implicit return.

    Destructuring works the way you want, e.g. (SpiderMonkey js shell, same vintage as Firefox 4 b10pre):

    js> line = “foo=bar”
    “foo=bar”
    js> let [key, value] = line.split(“=”, 2);
    js> print(key, value)
    foo bar

    I’m not sure what warning you used to get, but this works the way you want too:

    js> line = “foo”
    “foo”
    js> let [key, value] = line.split(“=”, 2);
    js> print(key, value)
    foo undefined
    js> options(‘strict’)
    “anonfunfix”
    js> let [key, value] = line.split(“=”, 2);
    js> print(key, value)
    foo undefined

    Global object properties can be accessed as you’d hope, perhaps even via window.gprop, etc. In reply to @Mook, I sketched DOM.window assuming a module with declared name DOM, but in a browser embedding the window (aka self) default import should be there.

    Again removing objects as scope frames does nothing to restrict your access to those objects by other means. It does prevent free variables from referencing global object properties, and that’s a feature.

    /be

  11. You probably don’t want to call them “hash-funcs”, brendan, because that has the obvious clash with “hash functions” that compute hashes.

    You should probably copy ruby terminology and call them “Blocks” or “Procs”.

  12. I like (most of) it!

    Additional wishes:
    – Self-style prototypal inheritance (*not* much different from how JS is now): https://www.2ality.com/2011/01/going-completely-prototypal-in.html
    – Python-style keyword arguments (but I do agree that destructuring makes them less urgent)

    Things I would change in your proposal:
    – Handle “this” like Lua: obj.method(a,b) should be syntactic sugar for m(obj, a, b) (where “m” is the function stored in obj.method).
    – if statement: I would make braces mandatory and introduce an elsif keyword (could also be named “else if”)
    – Implicit returns: It would be nice if the same could be done for if-then-else (i.e., it becomes an expression). No more ternary if operator!
    – I’ve critiqued CoffeeScript’s syntax and compared it to JS without parens: https://www.2ality.com/2011/01/coffeescript-versus-javascript-without.html

  13. @Liam: touché! I always say “hash function” and spell it out; plus, context helps. But hash-func is too close, you’re right. “Block” is used for the {…} compound statement, and a bit different in Ruby (due to Smalltalk — see the Tennent’s Correspondence Principle reference about lambdas). Still not sure what to call these things. “Hash-procs”?

    @Neil: good point! I’ll talk with @Alex and others at this week’s TC39 meeting. Compatibility is hard to break here, although browsers differ on the result of, e.g.,

    javascript:alert(document.getElementsByTagName(“a”).constructor)

    Possibly we need a new way for a given object delegating to the Array generic methods to ask for the result to be other than a fresh Array.

    @Axel: |this| uncurrying is something I’d like to explore, but it would need new syntax for the “method” in question. What you propose is a big compatibility break.

    The expression language idea is attractive but challenging to make safe in the face of ASI (see exchange with @Jeremy above), and possibly a “bridge too far”.

    On prototypal-only, Harmony is not going to take away the new operator or function .prototype properties. Usage of these would have to fade over years or decades before any such excision, but they’re quite popular. JS is multi-paradigm and flexible on this prototype-vs.-constructor issue.

    I thought at first you might propose Self-like multiple prototypes (MI, mixins, other patterns: see https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.49.6562). That would be an interesting extension!

    /be

  14. Would the iterator’s variable scope change for Harmony as well?

    JS: for (var k in o) append(o[k])
    Harmony: for k in keys(o) { append(o[k]) }

    In JS, “k” acts like it’s declared in a scope that exists just outside the loop. So this means closures referencing “k” will get the last iteration’s value.

    So instead of writing:
    Harmony: for k in keys(o) { let K = k; append(#() K) }
    or..
    Harmony: [k for k in keys(o)].forEach(#(k) append(#() k))

    It’s just:
    Harmony: for k in keys(o) { append(#() k) }

  15. Brendan, first of all, amazing job! Although I don’t agree on everything you exposed, I do on pushing the envelope so that at least the best ideas survive.

    Here’s the opinion of a humble JS dev. There are lots of I’d love to see in JS. For instance, the … notation is awesome. But also there are two additions I’d make. First, a very simple one:

    let myArray = [];
    myArray[] = “hello”;

    We all know myArray.push(“hello”) is slower that myArray[myArray.length] = “hello” because push() expects more than one argument. myArray[] = “hello” would be simple, useful and it still looks like javascript.

    Now, the most common pattern besides modules that I use to avoid spaghetti code is the following:

    var MyClass = function() {
    MyClass.superclass.constructor.apply(this, arguments)

    /* use local vars and all the goodies inside functions */
    }
    extend(MyClass, SomeSuperClass); // Using object copying and all that, for example as in YUI’s Y.extend method

    This idea is very popular in JS frameworks such as YUI, ExtJS, etc. ES4 (omg I’m mentioned that which must not be named! :P) implemented too much OOP, so much in fact that the language lost the fantastic learning curve it has today. But I still wonder why shouldn’t I be able to write:

    class MyClass extends SomeSuperClass {
    }

    Wouldn’t it be a good idea to have that syntax do exactly what a dynamic class is in ES4 and as a shortcut for what constructors are today?

  16. I love these ideas Brenden. Granted I have some nitpicks and personal prefs but overall, seriously, if JavaScript would look like this in Harmony, I would be very happy! I honestly don’t like the idea of making JS into a language that can make other languages (e.g. proxying overloading, etc… everything), sorry, no offense. I like seeing stuff like this however, solving the problems and helping it being used more effectively as it is. Love it Love it. Got my vote.

  17. @Edward: fresh let binding per iteration, as proposed in paren-free.

    @Peter: thanks, will fix.

    @Juan: would += serve better as push shorthand? [] looks like an array initializer or empty array destructuring pattern, so w = xn [] = y would parse as w = (x[] = y) meaning w = x.push(y). Note there is no error correction here, so no automatic semicolon insertion.

    It is ironic that JS could not have class in 1995 because it would have rivaled Java. It was constrained by both time and a sidekick role. Since then, adding classes has been challenging. TC39 has considered classes for Harmony, but we have not settled on one approach. See the traits links in reply to Tim above.

    /be

  18. One thought I had, that is sorta a pain and many languages seem to offer solutions for (including CoffeeScript) is string interpolation, any thoughts about adding that to the dream? (e.g. let example = “43 is #{40+3}”

  19. @Brendan w = xn [] = y would parse as w = (x[] = y)

    My idea came from php. I thought at least one space would remain when parsing. But if that’s the case then += would work perfectly.

    Regarding classes, it’s great that there’s discussion about them. Since you were focusing on other parts of the language I thought they weren’t on the table.

    To be honest, I didn’t understand the traits document. Well, I did, but it took me a while.

    I don’t think JS needs complete OOP with interfaces, abstract classes and all that. It just needs a syntax that simplifies dealing with prototypes and creating objects. So constructor declarations (https://wiki.ecmascript.org/doku.php?id=strawman:obj_initialiser_constructors) seem to be more like what I was expecting. However, I think I’d try to keep it as similar as it is today as possible. I’m going to write a blog post about it because I have too many opinions for a comment.

    Thanks for listening and answering!

  20. @Marc: string formatting has been underserved by TC39. We had some ES4 ideas:

    https://wiki.ecmascript.org/doku.php?id=discussion:string_formatting

    and more recently, in the Harmony era:

    https://wiki.ecmascript.org/doku.php?id=strawman:quasis

    More work needed. Please feel free to make concrete suggestions and proposals on es-discuss.

    UPDATE: I forgot @Crock has a new proposal up:

    https://wiki.ecmascript.org/doku.php?id=strawman:string_format

    /be

  21. on “tail”: why replace one implicit arcane knowledge with another? that’s plain wrong.

    why not make it mandatory. Like python’s self argument to object’s methods.

    the syntax should require a return value for a function. or fail. not ASSUME that the last line should be returned. That’s just wrong. And used by terrorists. think of the children.

  22. Brendan, in the new arguments, is there any way for a function to call itself recursively without resorting to closure variables? Used to be you could call arguments.callee but I’m not seeing anything like that in the brave new world.

  23. Good read – thanks!

    I would personally prefer to see ES6 make small, “safe” steps, sooner rather than later, and perhaps put bigger stuff off until ES7.

    I would truly love to see rest parameters, default parameter values, spread, destructuring, and those missing library links. I could probably wait on the other stuff.

    Kevin

  24. @Gabriel: make what mandatory? “return”? Your absolutism is terrifying children. CoffeeScript and other expression languages have implicit return values and the world’s still here.

    @Anthony: of course a function can call itself without using arguments.callee, since ES3 (and ignoring a bad IE bug, fixed in IE9): use a named function expression.

    @Kevin: we are making steps that amortize the high overhead of writing and standardizing a new spec.

    Also, too many small steps (besides being eaten up by that overhead, so not arriving much sooner) often enshrine mistakes we can’t back away from later.

    We’re prototyping before spec’ing, which takes some time but gives us implementor- and user-tesing results we need to make a better spec, and a spec that browser vendors will even consider implementing. @andreasgal prototyped Harmony proxies in a weekend; modules next!

    /be

  25. @MySchizoBuddy: trunc, hypot, divmod, a few others (hyperbolic transcendentals?), see

    https://mail.mozilla.org/pipermail/es-discuss/2009-March/009036.html

    The operators/literals/value-types dark horse link in my blog (follow it) is probably post-ES.next but still Harmonious. Just needs time. Matrices done using it would be immutable and have operators.

    If you want mutable matrix objects, there’s a chance operators as methods (double dispatch for binary ops) will happen independently of the value proxies.

    Your best way to help at this point is to state your requirements precisely and show use-case examples on es-discuss@mozilla.org.

    /be

  26. I like your dream, having loved JS since I was eleven. However, believing that JS is the best language in the world, I think return shouldn’t be thrown away completely, it’s handy thing to have, so I’d hope for a mixed approach.

    That said, JavaScript’s strength is in its flexibility, so were I the one to decide what to add, I’d add more flexible primitives so that we could use operators with custom objects (and arrays too) , e.g. a = {x: 4, y: 4}, b = {x: 2, y: 3}, x+y

    I’ll come back with more later, I’m on cell right now.

  27. Really like the “hash forms” – ingenious. Kills two birds with one stone:
    – succinct semantic richness for developers
    – minifies nicely for ES as assembly

    Re: (Opt-in is required for Harmony because of new syntax…)

    Do these new hash forms – and parens free – require a new explicit opt-in?
    Or is it only new (non-reserved) keywords that require the opt-in? e.g module.

    So in the spirit of the succinct hash form:

    // two hashes = module
    ##MyModule {
    blah
    }

    Leave new lengthier keywords for alt-JS’s.
    The global object issues could be covered with a pragma:
    “use strict noglobal” or “lexicalglobal” or “frozenglobal”

    The rolling feature release seems popular. It fits in with the rolling release approach of Chrome (and i think Firefox) where new features emerge and developers must probe for them. Both ES5 and HTML5 features – both of which do not have explicit versions.

  28. @Skierpage: thanks, corrected — but note that my link was correct when first published, and until today, when proper tail calls moved from strawman to harmony status based on this week’s TC39 meeting approving it. This green-lights proper tail calls for prototype implementation. How is that for fast progress?

    @Jussi: return is legal in #-functions, you can write it at tail position without problem and it is necessary for early returns (to avoid the problem some of us recall from Pascal functions where you have to torture your control flow with if-else chains instead of casting out unlikely cases with early returns).

    Operator methods may happen, see my reply to MySchizoBuddy. They are on Allen Wirfs-Brock’s list of strawmen to work on before the ES.next cut-off (which is not the Harmony cut-off; we might need two ECMA-262 editions to get to the promised land).

    @Kevin: without opt-in per RFC4329, old browsers will choke on new syntax, including #. This will stall page rendering and developers will want to avoid it, so the least we can do is provide script type=”harmony” (that’s a placeholder, of course) opt-in. But we can do better. More on versioning another time.

    /be

  29. I mentioned “this uncurrying” in reply to @Axel above, and the question of how “lexical this” works if the #-function named f is called not via f() but via o.m() for o.m=f can bind |this| to o came up at this week’s TC39 meeting.

    There is concern on the committee that lexical and only lexical |this| binding means someone could get their hands on the function (if it is exposed as a method) and then call it to get at, or at least to mutate, a |this| object that should not be available to the caller — the outer lexical scope’s |this|.

    Of course, we want #-functions to be usable as ad-hoc methods too, just as functions are in JS, with o.m() binding |this| to o and doing whatever it is supposed to do with that |this|.

    So Dave Herman proposed at the meeting that #-functions must declare |this| as their first formal parameter to be used as methods. Mark Miller and I then said “and if a #-function does not declare |this|, it is only a function and |this| is always lexical”. (Really, we said that at the same time and in unison. Half-kidding. 😉

    So #foo(this, bar, baz) { this.quux = bar + baz } is meant to be called as a method. If you call it directly via foo(1,2), |this| binds to undefined (and for this function you would get an exception attempting to assign 3 to undefined.quux).

    But #foo(bar, baz) { this.quux = bar + baz } inherits |this| from its lexical scope, and that lexical |this| binding cannot be overrridden by calling foo via a property reference such as o.m().

    EIBTI, the Zen of Python says. Saying what you mean is important in some cases. JS made do with functions as methods only because of my haste in ’95, and people do get burned by “wrong |this|” bugs. Explicit |this| parameterization helps.

    At this week’s meeting we also seemed to agree that #foo(){} binds foo as a const by default, so const #foo() {} is redundant, and if you want a writable binding, use var or let.

    We also possibly agreed that #-functions are not constructors, but this hinges on what we do to make constructors as sweet as #-functions here:

    https://wiki.ecmascript.org/doku.php?id=strawman:obj_initialiser_constructors

    /be

  30. Completeness attack: Allen Wirfs-Brock mooted the idea of

    #foo(this: bar, baz) {…}

    or perhaps a different punctuator than comma or colon, since the number of formal parameters seems off-by-one compared to the call expression foo(1, 2). Formal parameter syntax is restricted enough even with destructuring that we could use | or similar chars.

    Not sure what syntax is best, but the idea of declared-|this|-param seems good.

    /be

  31. brendan,
    first, thank you for your dedication to the open web, and especially for always sharing your perspective so candidly with the public on your blog, on mailing lists, and at conferences.

    =======================
    REGARDING STATIC TYPING
    =======================

    since es4 was shelved more than two years ago, opt-in static typing seems to have been removed from the javascript roadmap. the “Proposal for types in Harmony” hasn’t changed since sept 2008 (https://wiki.ecmascript.org/doku.php?id=strawman:types&s=static+type). and “Classes with Trait Composition” seems to have “semi-static errors” only (https://wiki.ecmascript.org/doku.php?id=strawman:classes_with_trait_composition#semi-static_rejection_rulessimple_inheritance).

    without opt-in static typing, javascript ides cannot accurately or easily provide:

    * code hinting (aka intellisense, code completion)
    * code hyperlinks (e.g., cmd+click on a method reference to jump to its definition)
    * guaranteed refactoring
    * auto-generated documentation (jsdoc-toolkit et al try their best, but none are perfect)
    * compile-time reference errors (e.g., a typo in a method name goes undetected until it is evaluated.)

    javascript’s lack of static type errors leads to very subtle bugs, such as a function behaving differently if it is passed a string or an array. for example:

    function foo (bar) { alert(bar[0] }
    foo(“abc”); // yields “a”
    foo([“abc”]); // yields “abc”

    in my own experience, in maintaining a 10,000-line javascript library, i have used aptana, komodo, and jetbeans idea. all of those ides try desperately to provide code intelligence for javascript, but none of them even comes close to the intelligence provided for statically typed languages. these days, visual studio goes so far as to execute your javascript code in an attempt to add to intelligence, but it’s still not perfect, and i get the impression it never will be without static typing.

    ide code-intelligence, compile-time type errors, and auto-generated docs are critical to my workflow. they also seem critical to google, who implemented static-type checking in closure compiler in order to “produce JavaScript that is less buggy and and easier to maintain.” presumably google hit a wall attempting to manage the development of gmail and google docs with plain javascript. others have hit the wall too. see:

    * jangaroo (“JavaScript 1.x developers need most desperately [features] for object-oriented programming ‘in the large'”)
    * haxe (“Access the typed browser DOM APIs with autocompletion support, and all the dependencies are resolved at compilation time”)
    * script# (“Productivity and better tooling are primary motivators behind Script#”)

    unit tests, of course, help, but in practice unit tests are often too time-consuming to maintain at 100% coverage simply for the sake of static type-checking.

    so, some questions:

    S1) in november 2007, you said “JS users deserve an optional type system” (https://brendaneich.com/2007/11/my-media-ajax-keynote/). have you changed your mind? are you now philosophically opposed to adding *opt-in* static typing to javascript (ala es4 or actionscript 3)? if not, is tc39? if no one is opposed, then when is static typing planned? and why was it deferred until post-es5 in the face of such widespread demand for better productivity and tooling? why are the features in es5 more important than static typing for large-scale software-development productivity?

    S2) if you are (or tc39 is), in fact, philosophically opposed to *opt-in* static typing, could you please explain why? es4’s (and smalltalk’s) opt-in static type system seems to give programmers the best of both worlds. isn’t “use it if you want it” a good thing for everyone?

    =======================
    REGARDING CLASSES
    =======================

    i agree with joan. despite the fact that the es5 spec says “ECMAScript does not use classes,” nearly everyone still uses the term “classes” when talking about constructors+prototypes in javascript. for example, here’s the “Prototype” framework’s api doc page for its Hash “class” (note the–strictly speaking incorrect–use of the terms “class” and “instance method”):

    https://api.prototypejs.org/language/Hash/

    nearly every popular javascript framework includes wrappers for making javascript look more like classical oop (e.g., by adding “extends()” and “inherits()” functions). so there seems to be nearly universal industry support for classes as a conceptual tool.

    as a daily javascript programmer, it’s very unnatural to think and read “class” everywhere in the javascript world, but then write code like this:

    MyGridPanel = Ext.extend(Ext.grid.GridPanel, {
    constructor: function(config) {
    var store = new Ext.data.Store({…});
    var colModel = new Ext.grid.ColumnModel({…});
    config = Ext.apply({
    store: store,
    colModel: colModel
    }, config);

    MyGridPanel.superclass.constructor.call(this, config);
    },

    yourMethod: function() {
    }
    });

    or this:

    goog.inherits = function(childCtor, parentCtor) {
    function tempCtor() {};
    tempCtor.prototype = parentCtor.prototype;
    childCtor.superClass_ = parentCtor.prototype;
    childCtor.prototype = new tempCtor();
    childCtor.prototype.constructor = childCtor;
    };

    surely this:

    super(config);

    would be faster to read, write, and maintain than this:

    MyGridPanel.superclass.constructor.call(this, config);

    without classes as first-class citizens in javascript, every developer has to either brave the dark waters (go look at the source for Ext.extend()!) of prototype-style inheritance or pick a religion in the library holy war when adopting a class-ish javascript implementation. the current multiplicity of oop dialects in the javascript field (see mootools, ext js, jquery, dojo, prototype, closure, yui, etc) reduces skills transfer, prevents tools from providing core oop coding infrastructure, and imposes a migration tax on developers when moving between projects or companies.

    so, +1 for *optional* classes.

    and, questions:

    C1) in november 2007, you wrote: “[w]hatever you call them, something like classes are necessary for integrity properties vital to security in JS2, required for bootstrapping the standard built-in objects, and appropriate to a large cohort of programmers. These independent facts combine to support classes as proposed in JS2. […] using closures for modules and class-like abstractions is verbose and clumsy compared to using new syntax […] I really do believe these things”.

    but in january 2011, you now write:
    “adding classes has been challenging.”

    have you changed your mind about classes being necessary? is there a summary somewhere listing the reasons that “adding classes has been challenging.”?

    C2) is adding classes just a matter of time/effort, or is there philosophical opposition to classes among tc39? if there is opposition, is there a summary somewhere of the rationale for opposing (optional) classes? what is holding tc39 back?

    in an ideal world, i would love to see succint, official philosophical statements published by tc39 on these (and other) key issues. how about starting “ecmascript.org/philosophy”? there are literally millions of javascript programmers affected by the committee’s research everyday, and without being a language expert, it is disorienting keeping up with all the changes and justifications without synopsis-style descriptions of important tc39 positions.

    =======================
    REGARDING BOUND METHODS
    =======================
    big +1 for automatic method binding if classes are ever introduced into javascript. i.e.,

    // c is a method of b
    var cref = b.c;
    // in cref, ‘this’ refers to b
    cref();

    i’m very glad to read that ‘this’ improvements are on the radar.

    =======================
    REGARDING “JAVA SUCKS”
    =======================

    while you are dreaming of a better javascript in this blog post, some of your vision clearly responds to what you see as failures in other languages, particularly java. at jsconf 2010, in “Proxies are Awesome,” you said bluntly “java sucks”:

    https://jsconf.eu/2010/speaker/be_proxy_objects.html

    douglas crockford, another vocal tc39 member, also performs similar drive-by insults.

    some questions on your view (and tc39’s view) of java:

    J1) when you say “java sucks” do you mean “java sucks compared to es5” or “java sucks compared to language x” (where “language x” is, i’m guessing, smalltalk)?

    J2) do you think es5 is a “better language”* than java for large-scale software development? if so, can you explain why, preferably with case studies as evidence? maybe you already have an article explaining your rationale? i’m interested in understanding why, in your experience, you feel es5 (or “language x”) is a better choice than java (or, say, c#) for large-scale software projects. ideally, i’d like to read an in-depth, side-by-side comparison of a project that failed or was unmanageable in c# or java, and then became easy to maintain after a switch to javascript (or “language x”).

    J3) if you think that es5 is not yet a “better language”* than java for large-scale software development, what features do you feel are required to make it one?

    * i can think of only two ways to measure a language’s quality: a) developer productivity, and b) the amount of mental complexity the language allows a human to cope with. if you are judging java and es5 by other criteria, what are those criteria?

    tc39 is the curator of a programming language that deeply affects human society–the entire web relies on it. i believe that as javascript’s curator, the committee has a responsibility to share the motivations of its philosophical positions on javascript-language design. while others may expect more professionalism than “java sucks” from the steward of such an important human resource, to my taste, “java sucks” is as good a summary as any other. but if “java sucks” is going to be a public refrain (and ostensibly even a guiding design principle), i believe it behooves tc39 to publish a list of what it feels are java’s failures–and the ways javascript purports to overcome them. a published rationale would help educate the entire community, and elevate the discussion above mud slinging to a reasoned effort to advance computer science. how about ecmascript.org/philosophy/thoughtsonjava? ; )

    =======================
    ECMASCRIPT ROADMAP
    =======================

    it would be very nice to have an official public list of the upcoming features being planned for the next version of ecmascript at any given time. with enough digging, i found https://wiki.ecmascript.org/doku.php?id=harmony:proposals but that document seems es5-specific. i sure wish there was an concise, always up-to-date ecmascript.org/roadmap/.

    thank you again sincerely for your prolific contributions to the web. your work and writing is always enlightening.

    colin

    ps. sorry for the no-caps…i have pretty severe rsi, so i don’t like using the shift key.

  32. @Colin, thanks for the kind words. Your comment was long enough that I wondered if you might rather post it on a blog, but I moderated it through and I’ll reply briefly here:

    ES4 never figured out how to have optional static typing for the web. From 2007 on we only ever proposed dynamic checks (“like” is not even a type annotation). See Thorn (Vitek et al., Purdue) for research developing these ideas, including “like”, but please realize that static typing was not part of ES4 as proposed for “Web JS”.

    Dynamic code loading can invalidate static type judgments, this is one of the big problems with trying to add static typing, even as an option, to Web JS. AS3 in Flash can require that the Assembly (or whatever it’s called) in the .swf file be complete and type-checked, but browsers cannot do that with source JS, even ignoring eval. And we cannot ignore eval.

    Typed Racket (Sam Tobin-Hochstadt) does offer static types with inference, but only in a Racket module, which has contracts on the outside to enforce the typing with good blame when dynamic Racket (originally called PLT Scheme) modules pass values in that would violate the static type checks.

    So to have static types even as an option for JS, we’d need a similar module system to Racket’s. Fortunately, simple modules proposes that system and (provided TC39 members agree) it looks headed for Harmony.

    But I question static types for JS in light of DoctorJS and our “GoedelMonkey” (I just made that up) hybrid type inference work created by Brian Hackett for the next version of Spidermonkey. We are finding that both advanced control-flow analyses such as the CFA2 used in DoctorJS, and the clever online semi-static with re-JITted analysis work of bhackett, make it unnecessary in most cases for developers to write down type annotations.

    With these tool-based static analyses, and online hybrid analyses, features like typedown or intellisense are here, today, without static typing. And the win of not requiring all developers to annotate types is huge. AS3, especially under early tools like “Coach Mode”, is IMO excessively annotated, and this is a productivity tax on its developers, even ignoring the loss of dynamic type programming wins.

    So, yes: I have changed my mind since November 2007. I’m not convinced JS developers would benefit from an optional static type system. But my position is a bit more nuanced than that there ought not be such a system. There ought to be many variant dialects, analogous to Racket’s module-wise language support. Dave Herman is working on this and we hope it can accomodate a module written, e.g., in CoffeeScript.

    For Typed JS along the lines of Typed Racket, we would need not only modules but contracts. That is not yet proposed, but it could happen for a later Harmonious edition than ES.next (ES6, unless something needs that number in a hurry).

    I hope this answers S1.

    As for S2, I can’t speak for TC39 members on this deep a topic. I hope they all agree with me on the module system paving the way for language-lab experiments of the kind done with Racket, which may indeed allow for contracts and then something like Typed JS. But some TC39ers I know probably object to any standardized Typed JS. Time will tell.

    You mentioned Smalltalk’s opt-in static type system, but that was Strongtalk (Gilad Bracha did the theory; see https://strongtalk.org/), and AFAIK no used-at-scale commercial or even other research Smalltalk implementations ever supported optional types. Allen knows all, I’m sure.

    Regarding C1, my statements are not in conflict. Classes have been challenging to retrofit, and the bootstrapping use-case is not driving current efforts. But I’m patient and hopeful. What I recall some of us on TC39 saying is that if ES.next were ready but for classes, at any state of spec production starting last year, we would not hold the train for classes. I do agree with that.

    On C2, it’s hard to say what is philosophy and what is pragmatics, but the challenge of adding classes involves both. We want to avoid static nominal typing. We won’t add single-inheritance classes with abstract mixins (interfaces) a la AS3.

    The least some folks propose are “zero-inheritance” classes just for runtime “type” guards, as a monotonic bound on mutation of any value referenced via the guarded variable that would violate the class’s declarations. I find this idea too minimal. JS devs already contrive various forms of inheritance and would find zero-inheritance classes unhelpful if not useless.

    You can tell from what I’ve written here that classes for some TC39ers are entangled with some notion of runtime type annotation. See

    https://wiki.ecmascript.org/doku.php?id=strawman:guards

    for the current proposal, still in progress.

    You note how millions of developers depend on TC39 to improve the JavaScript language according to diverse criteria, but that is a big, obvious part of the adding-classes challenge, too. Developers do not agree on which kind of classes, with what exact semantics, or whether to add anything like classes at all. Ditto guard annotations.

    TC39 cannot design by committee well, or at all. Even worse would be designing by focus group or copying AS3 (which in many ways copied Java pre-generics, by looking in the rear view mirror at what Enterprise developers seemed to want). This, I am firmly against.

    I’m out of time, but I hope this helps.

    /be

  33. @Colin wrote “it would be very nice to have an official public list of the upcoming features being planned for the next version of ecmascript at any given time. with enough digging, i found https://wiki.ecmascript.org/doku.php?id=harmony:proposals but that document seems es5-specific.”

    No, that is the list of harmony:proposals green-lit for implementation prototyping and likely to be in the next edition.

    ES5 is done (and then some: the ISO version is ES5.1 — see the https://ecmascript.org/ front page). We are in “onward to Harmony” mode now.

    /be

  34. Woah! This thread is becoming exponentially interesting.

    I spent a couple of days browsing through the ES mailing list, looking at how you work and a couple of specific topics. Needless to say, I’m utterly surprised by it. It’s amazing how openly you discuss every nook and cranny of the language and, as Colin remarks, the good predisposition you show.

    Answering Colin’s question about what traits define a good language, I’d says there are many more than just productivity. One of them that I particularly like about javascript is its learning curve. Unfortunately there’s the DOM mess, but we’re moving forward in that area as well.

    It is my personal opinion (I’m not expecting everyone to agree with me on this) that javascript is the best language _for the Web_. Accepting this is both a restriction and a blessing. It means I’d never expect JS to be the better language for writing physics numerical approximations, but it also means that I don’t expect it to use the standard floating number type. What I do expect from javascript is for it to have a really cool learning curve, because the Web is more ubiquitous every day and so it should be as easy as possible for anyone to build something with it, same as producing content. This IMHO means it should remain dynamically typed and with an equilibrium between simplicity and power (that’s why I’m not sure about the idea of having two different types of functions). It also means it should help developers avoid the bad parts, and thus the strict mode and lots of the changes in ES5 are very much welcome.

    Regarding classes, following this precept I believed they should behave similarly to what we’re writing today and call them “classes”. And so I expected them to look like something along the lines of…

    class ToggleButton extends Button {
      var foo = "foo"; //no need to especify private
      public bar = "bar";
      public baz() {
        return "baz";
      }
    }
    

    In my browsing through the archives I found that something similar to this was proposed https://wiki.ecmascript.org/doku.php?id=strawman:classes_as_sugar. Brendan please correct me if I’m wrong, but I believe this was dropped for a couple of reasons. One seems to be the “guard” thing that you mentioned in the last comment (I admit that I’m 100% ignorant in this matter). Another seems to be that some believed that if they were going to introduce new syntax it should also introduce new features and not just a new writing style. So it seems as if https://wiki.ecmascript.org/doku.php?id=strawman:obj_initialiser_constructors is the latest work.

    I have two concerns about that proposal (again, correct me if I’m wrong, which is very likely).

    1) There isn’t a simple way of chaining prototypes and assigning properties to the new one. constructor FakeArray () {} only seems to share the prototype with Array, so a change on FakeArray’s prototype would be a change on Array’s (bad idea). So it would have to be something like

    #objetize(super, p) {
      #F(){}
      F.prototype = super.prototype;
      var f = new F;
      for x in p { // paren-free :D
        f[x] = p[x];
      }
      return f;
    }
    
    constructor FakeArray() {
    }
    

    Using, again, to a function for inheritance, which kind of defeats the purpose.

    2) It’s even more verbose than the current syntax because it enforces the “this” keyword for private members and so makes minification less efficient because it can’t replace the private variable’s name.

    PS: clearly I wouldn’t hold the train for classes either. Modules are just too good. Security should always come before sugar.

  35. be:
    “Your comment was long enough that I wondered if you might rather post it on a blog”

    ya, i thought about that, but i figure it’s nice to keep the discussion self-contained. es-discuss didn’t seem like the right place for this level of conversation, but let me know if you’d like to move to a thread there.

    at any rate, i really appreciate your responses. here are a few follow ups…

    be:
    “Dynamic code loading can invalidate static type judgments, this is one of the big problems with trying to add static typing, even as an option, to Web JS.”

    i’d like to hear more. do you have a couple of simple practical examples of those invalidated judgments? (a link to a pertinent thread on es-discuss is fine.)

    be:
    “So to have static types even as an option for JS, we’d need a similar module system to Racket’s. Fortunately, simple modules proposes that system and (provided TC39 members agree) it looks headed for Harmony.”

    modules would be very very welcome. particularly support for cyclic dependencies. great stuff.

    be:
    “But I question static types for JS in light of DoctorJS”

    i gave this code to DoctorJS:

    function getFirstElement (param) {
      if (Object.prototype.toString.call(param) == "[object Array]") {
        return param[0];
      }
    }
    getFirstElement("s");
    

    DoctorJS gave me:

    1: getFirstElement : function(string) → any

    DoctorJS doesn’t know that getFirstElement() requires an Array, which means that an ide based on DoctorJS wouldn’t give me code hints for ‘param’. and my attempt to pass getFirstElement() the wrong kind of data would go unnoticed. a doc generator based on DoctorJS would say that ‘param’ is type string, which is wrong. am i using DoctorJS incorrectly?

    for comparison, consider the as3 equivalent (just as an example; i’m not claiming as3 is the world’s best programming language, it just happens to have syntax like javascript so it provides a good comparison). in eclipse, suppose i write this (easier-to-read-and-write) code:

    function getFirstElement (param:Array) {
      return param[0];
    }
    getFirstElement("s");
    

    i instantly get a red (x) in the margin telling me that getFirstElement() doesn’t accept strings. not only did i write fewer keystrokes with static types, but eclipse instantly resolved an error that would have gone unnoticed and might been deployed unless my app had full unit-test coverage. and, of course, in eclipse i get perfect code hinting, guaranteed-to-be-accurate documentation, and guaranteed-to-be-accurate refactoring.

    can DoctorJS really do all that?

    be:
    “We are finding that both advanced control-flow analyses such as the CFA2 used in DoctorJS, and the clever online semi-static with re-JITted analysis work of bhackett, make it unnecessary in most cases for developers to write down type annotations.”

    isn’t “in most cases” the point? my preceding DoctorJS example doesn’t seem to work. isn’t re-factoring or early type-checking useless if it only works most of the time? if i refactor a method name in a 20000 line program, and the refactor is 99% accurate, the failed 1% leaves me with latent bugs. i just found two such bugs today from a failed javascript refactor, where a rename from .getWidth() to .width left a few nasty stragglers that were caught only due to the human labour of qa testers. the .width-refactor would not have failed with type annotations.

    this is practical stuff, not academic philosophizing. i hit these issues every day in javascript, and always walk away thinking “wow, i just lost an hour looking for a typo when i could have been programming”. shouldn’t computers do things like renaming for us? sure, i’d invest a little time writing opt-in type annotations (mostly made trivial by code hints), but i get that investment back many times over every time i refactor, use intellisense, find typos early, and generate accurate docs.

    be:
    “With these tool-based static analyses, and online hybrid analyses, features like typedown or intellisense are here, today, without static typing.”

    in which tools? i don’t know of a single 100%-perfect-intellisense implementation in a currently available javascript ide. do you? (you have no idea how much i hope you do ; ).

    accurate documentation generators (commonplace in languages with static types) are not here today. jsdoc-tookit regularly fails me, and yuidoc by design doesn’t even try to infer code content (as yahoo’s stephen woods says, “[with yuidoc y]our code is going to fall out of sync — I guarantee it, it’s inevitable” https://developer.yahoo.com/yui/theater/video.php?v=woods-yuiconf2009-yuidoc).

    are you saying that type annotations are not necessary for 100% accurate intellisense, refactoring, early type checking, and documentation generation? if so, why aren’t those things available today, more than 15 years since javascript’s inception? and if DoctorJS gives javascript programmers all the early type-checking they need, why didn’t google just make something like DoctorJS instead of closure?

    be:
    “And the win of not requiring all developers to annotate types is huge.”

    i absolutely agree that type annotations should be optional. i’m specifically talking about *opt-in* type annotations, which would not be required by all developers.

    be:
    “AS3, especially under early tools like “Coach Mode”, is IMO excessively annotated, and this is a productivity tax on its developers, even ignoring the loss of dynamic type programming wins.”

    to be clear here, as3’s type annotations are 100% optional, so there is no productivity tax to pay. quite the contrary: developers willingly “buy” the benefits of static typing by opting into it. all annotations are opt-in, and you can opt-in or out on a per-item basis (e.g., by annotating only two out of four function parameters). in fact, as3 also still fully supports prototype-based programming, ala javascript 1.x. but to be honest, despite still being fully supported, i haven’t seen prototype used in any as3 production frameworks code in years. the community seems to have universally chosen classes.

    i haven’t done any formal studies, but if you are willing to accept one very active programmer’s anecdotal experience: i programmed in dynamic as1 (ecma-262 3rd ed) for 5 years without static types, and then as3 for 5 years with static types. i’d say as3’s static types have vastly increased (doubled?) my productivity and my ability to maintain complexity and ship reliable software.

    be:
    some TC39ers I know probably object to any standardized Typed JS.

    i’d love to read some example-based rationale and case-studies by those opposed. do you have any links to position statements by those members? too much of the static-type discussion seems to amount to a cheerleading competition. i have no affiliation to any one language or methodology. i’m only interested in tools that improve my efficiency.

    be:
    Regarding C1, […] What I recall some of us on TC39 saying is that if ES.next were ready but for classes, at any state of spec production starting last year, we would not hold the train for classes. I do agree with that.

    cm:
    yup. 100%. good call.

    be:
    You can tell from what I’ve written here that classes for some TC39ers are entangled with some notion of runtime type annotation. See
    https://wiki.ecmascript.org/doku.php?id=strawman:guards

    this work seems interesting. would it provide the information required by tools for code hinting, refactoring, early errors, and documentation generation? it would be great to see a few more practical examples on these proposals. an example paints 1000 words, and allows the community to stay more involved with the language’s evolution.

    be:
    Developers do not agree on which kind of classes, with what exact semantics, or whether to add anything like classes at all. Ditto guard annotations.

    yup. this stuff isn’t easy. it wouldn’t be any fun if it were ; )

    be:
    TC39 cannot design by committee well, or at all. Even worse would be designing by focus group or copying AS3

    agreed. as3’s requirements are very different than javascript’s. they are close enough, however, that it still seems unfortunate that they couldn’t have found a way to follow one core spec.

    be:
    ([AS3] in many ways copied Java pre-generics, by looking in the rear view mirror at what Enterprise developers seemed to want). This, I am firmly against.

    you’re casting java as a failed, outdated technology again. i’m still waiting for your rationale article. maybe a “java sucks” post to complement “threads suck” (https://brendaneich.com/2007/02/threads-suck/)?

    thank you again for the discussion. few in your position in computer science are as approachable to the general programming community.

    colin

  36. @juan i agree that approachability is important in a language, particularly so in javascript. my point is that javascipt already does a great job of being approachable. now it needs some power tools for large-scale software development. adding *optional* type annotations does not make javascript less approachable. simple scripts will still use a few functions and variables with no classes, and no annotations. optional classes and annotations will give programmers room to grow with the language as their projects increase in complexity.

    i also agree that javascript is good for the web. but, slowly but surely, it is moving into other environments. the decisions being made now deeply affect language’s 5-10 year trajectory. think of the children!

  37. In re Juan : “It is my personal opinion (I’m not expecting everyone to agree with me on this) that javascript is the best language _for the Web_. Accepting this is both a restriction and a blessing. It means I’d never expect JS to be the better language for writing physics numerical approximations, but it also means that I don’t expect it to use the standard floating number type. ”

    That’s well and good, and I agree. The problem however, is that increasingly there is a movement to use JavaScript *everywhere* and not just on the web. There’s real momentum out there amongst people who want it to be a Lingua Franca of programming in much the way C has been so regardless of whether you’d expect JS to be a good language for writing numerical approximations or physics engines there’s a chance that you’ll be doing just that whether you want to or not… and in its current state (and indeed even in the future state envisioned above) I’d really rather not.

  38. @Anon: did anyone get a choice with C?

    Bignums are wanted along with many other numeric types. This area has some momentum for Harmony, thanks to Allen Wirfs-Brock (at last week’so TC39 meeting, Allen put his name down as champion).

    WebGL is here too, and games follow demos.

    So let’s make lemonade, and cheer up!

    /be

  39. @Juan: I’m the last person to oversell JS. For example, it’s not a good fit for systems programming. At Mozilla, we use C++, but it has enough issues that we’re investing in Rust for safety + concurrency that can map to much higher-degree hardware parallelism.

    JS breaking out on the server side via https://nodejs.org surprised me. At Netscape in 1995 we had server-side JS (LiveWire) along with the client stuff that stuck, but it was overwhelmed by the Java onslaught.

    Node wins because of a fast VM, but not only that: client-side hackers like using one language on both ends, and JS’s closures win for event handling compared even to languages like Ruby. People actually like JS outside of the browser, not just because of Node’s APIs or execution model or speed.

    Anyway, Node wasn’t my evil plan. But it happened and it looks like more than a flash in the pan.

    JS in the browser *was* my evil plan (and marca’s). It stuck, but only as a rush job, then stagnated by monopoly action and standardization, finally stretching its wings. So to your question: can we add a class construct that does what most JS hackers expect and roll by hand now?

    It would look like what you sketch, aka “classes as sugar”:

    class Derived extends Base {
      . . .
    }
    

    The |this|-free private member access is a win. Private by default was agreed to in 2008 fall. What went wrong?

    Nothing, really. This plan may make a come-back, but with some traits or mix-in support. The constructor object initialiser extension should merge or subsume, somehow. The guard idea depends on the class/trait/constructor proposal, and should not be rushed, so it’ll come after.

    I expect we’ll hear more on classes and traits soon on es-discuss.

    /be

  40. I like the idea of guards/annotations on api boundary’s only – on function signatures (in modules most usefully). Type inference can be used to flow through to vars etc in the function.

    Kills two birds with one stone:
    – devs can document the semantics of their code – for api users
    – more efficient code gen. Especially with per function code gen.

    // Using is and if
    #myFunc(x is Number if x > 10) { …. blah }

    Guards on all vars – as with the wiki proposal – looks more tricky.

  41. @Brendan I’m a big fan of Node for exactly the reasons you mentioned. I’m a client-side coder so I love reusing code on the server. And the async nature of Node just rocks. When I said “the Web” I was also thinking about Node. But there are even other places where JS appeared and surprised me too. WebOS is the most impressive one.

    Yup, at least property descriptors are missing in the current class proposal. I’ll be checking es-discuss and making lemonade!

  42. I’m typically against the unnecessary ruby-ish / coffeescript-ish enhancements, although I would welcome rest params and spreading 🙂 just no crazy ambiguous grammar stuff please

  43. addendum:
    i just tried the javascript support in netbeans. results:

    ======================
    refactoring
    ======================
    when i attempted “Refactor > Rename” on a variable, i received an actual warning dialog that read:

    “JavaScript refactoring is approximate. Review all changes.”

    in an ide, refactoring is the *last* thing i want to be approximate. and “reviewing all changes” is specifically the thing i don’t want to waste my time doing. that dialog might as well read:

    “We couldn’t implement refactoring in JavaScript, so we’re going to search and replace on the variable name as a string instead, and hope we get lucky. We’re very likely going to miss some references, so instead of spending your valuable engineering time writing code, please manually test all paths in your application to find all the bugs this operation will introduce.”

    no thanks. i guess i’ll keep the old name.

    in tests, the refactoring was as unreliable as advertised. i renamed a method in a superclass and netbeans did not change the references to that method in the subclass.

    ======================
    code hints
    ======================
    code hinting/completion in netbeans appears to be based on brute force string-matching. in an instance method, when i type “this.”, the suggestions offered include every method and variable in every file in the source path. weeding through the several hundred suggestions is barely more efficient than just typing the method or variable name.

    given netbeans’ code-hinting implementation, it goes without saying that author-time error reporting is limited to a basic syntax check (jslint-style), and doesn’t warn about things like argument mismatches, undefined methods, etc.

    i think the only popular javascript ide i have not tried at this point is visual studio (because i code on a mac). and i have yet to find one that does a good job of author-time error reporting, code intelligence, refactoring, and documentation generation.

    [btw, no offense at all to netbeans team. they are clearly doing the best they can under the circumstances. netbeans’ behaviour is more or less the same as all other javascript ides i have tried.]

    colin

  44. Thanks! I really appreciate you sharing your sincere thoughts – it is quite refreshing to see the inventor of an incredibly popular language allow the users such an honest glimpse!

    I know the time for bike-shedding is not upon us, but could I ask you why you chose ‘#’ vs ‘^’ – since the caret is what objective-C uses for its closures and is being proposed for standardization in C.
    https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1451.pdf

    Is it purely a matter of aesthetics? If so, I guess i’m out of luck, since I find the ‘#’ quite ugly, abrupt and discordant with the rest of the aesthetics of javascript. But this is purely a subjective reaction based on my use of C/C++ based languages in which we are conditioned to tune out the ‘#’.

    Thank you again!

  45. Faisal: ^ is bitwise xor, a binary operator. We could make it a prefix of these “pounder” or (let’s say instead of hash, see above) sharp function expressions, but then we would have an ambiguity due to (the lack of) automatic semicolon insertion:

    a = b + c
    ^(d)
    {d*d}

    is a valid way to write

    a = b + c ^ (d);
    {d*d}

    (suppose d is a complex expression, requiring parentheses and motivating the split across two physical lines).

    Telling programmers to write ; before ^ if ^ starts an expression statement (as a caret-function called immediately, say) won’t work — programmers won’t hear and they’ll be surprised by this new requirement.

    We could try more complicated rules on when ^ can be a binary operator and when it would introduce a caret-function, but # is unused and free of this problem — and already proposed by Arv and Alex. # is a bit uglier, and ^ might be read as lambda (a misreading on several levels!), but ^ is also a bit visually “light” compared to #.

    Anyway, the main motivation is to avoid more grammar complexity. As I’ve noted elsewhere, TC39 won’t go for the kind of disambiguation pass on lexed tokens, followed by (Jison-generated) bottom-up parsing, that CoffeeScript uses. It could be there’s a decent top-down cover grammar for some of CoffeeScript’s syntax that does not require too much AST validation and relabeling, but it’s not worth trying to wedge that into JS.

    I’m not criticizing Coffee here — rather noting that it has its own committee-of-one, Jeremy, who can manage the complexity and (this is key) implement it in JS. TC39 members including browser vendors, but also folks like Crock, are a tougher crowd.

    /be

  46. Dan: a singleton slice is still a tuple, so in the example:

    tuple[0:1] === #[1]
    

    Perl and E4X try to DWIM with automagic conversion of list-of-one to the one element as (apparently) needed, but this bites back. Better for slice always to result in a tuple.

    /be

  47. After being a bit disheartened about ES4’s demise, I’m glad to see how Harmony is developing: we get new features and have a prototype implementation to figure out what works.

    – “this” and functions: A solution like Python’s “self” would not be OK? I would not make “this” as a first argument special in any way, just automatically filled in if a function is invoked as a method. If you invoke a method like a function, you would have to explicitly set “this” to null (if you wanted that to happen). That would restore symmetry with call() and apply(). If only the new hash functions do this, then it would not break legacy code.

    – Multiple prototypes would indeed be cool. A standardized way of *setting* the prototype would also help. Then one could program whatever inheritance scheme one is happy with. A standard inheritance scheme *is* still sorely needed by JS, though. Resig’s simple inheritance plus a Prototype.js-style $super argument, maybe.

    – Type guards look useful. Most JS APIs already write down parameter types and it always helps me if I can do it in my code. Short-term solution: standardize JavaDoc-style type annotations. That would help IDEs, static analysis tools, and API publishers.

    – As an aside, because it probably should not be added to JS: predicate/multiple dispatch [1] is perfect for languages that are used in a web context, because one can keep code and data (JSON!) completely separate.

    [1] https://www.2ality.com/2009/05/multiple-dispatch-fix-for-some-problems.html

  48. The hash forms look good. Just a note – when i was playing around with alt-JS’s i used backtick as the prefix e.g

    let a = `{x:1,y:2} // record/frozen object
    let b = {`x:1,y:2} // only x fixed, y deletable
    let c = `[1,2,3] //tuple
    let d = `Number[1,2,3] // typed array

    It’s a bit ‘lighter’ than hash – but that’s about it.

  49. @Axel: we do want #-functions lexical-this unless the #-function declares that it is a method with #(this[…]) (the […] is meta, separator syntax TBD). Lexical-this serves the use-case currently hobbled by var self=this; hacks, which we know is common. Not all functions are methods.

    Object.getPrototypeOf is in ES5.

    JavaDoc-style annotations aren’t easy to standardize (we have tried). They take oxygen away from the guards work, which is the real deal anyway.

    Multimethods were proposed for ES4 but we don’t yet have even runtime types on which to build them, and they’re complex. If JS can avoid growing more than one dispatch scheme, while still serving use-cases such as JSON matching (see refutable match link in the blog post), everyone wins.

    @Kevin: besides being “too light”, ` is wanted for quasi-literals. While it needs work, we have a strawman still going, from Mike Samuel:

    https://wiki.ecmascript.org/doku.php?id=strawman:quasis

    /be

  50. @Colin: let’s please leave Netbeans out of it. That small getFirstElement example is a bit contrived. In a real codebase, “good” calls would pass arrays and the inference would notice that. JS hackers have latent type systems in their head, in docs, and encoded in runtime checks.

    But of course, if you want a type annotation or guard, then there’s no substitute. I agree with that. Wherefore the Harmony guards proposal, such as it is (what the G in var x :: G = y; means may be pluggable).

    /be

  51. be:
    “if you want a type annotation or guard, then there’s no substitute. I agree with that. Wherefore the Harmony guards proposal”

    most excellent. i think that paints a bright future for tooling and large-scale software development in javascript. thanks for making your position clear.

    “let’s please leave Netbeans out of it.”

    i don’t think you can leave IDEs out of a conversation about software productivity, particularly when language design handicaps tools. but the point is moot if you’re in favour of guards, despite my interest in knowing why you think IDEs are (or just netbeans is?) irrelevant to this discussion.

    “That small getFirstElement example is a bit contrived.”

    i see, you’re saying this (more likely) code:

    getFirstElement(“s”); // bad
    getFirstElement([“s”]); // good

    will give me:

    getFirstElement : function(string | Array[string]) > string

    agreed, that’s better, but it still doesn’t tell me that “string” is definitely not allowed for param. it also doesn’t work the first time i call getFirstElement(), which is not a contrived thing to do.

    last month, i spent about an hour hunting down a bug caused by code exactly like the getFirstElement(“s”) example. i’ve been a “JS hacker” since 1996. i ride off-piste with the “types-be-damned” cool kids. my api is coded and documented by me, and i *still* wrote “foo” instead of [“foo”] because i’m human. i get the feeling that there are more JS hackers out there slamming into trees than you seem to think; but again, the point is moot if tc39 plans to implement guards.

    much obliged for all your responses.

    colin

  52. @Brendan “we do want #-functions lexical-this unless the #-function declares that it is a method with #(this[…]) (the […] is meta, separator syntax TBD). Lexical-this serves the use-case currently hobbled by var self=this; hacks, which we know is common. Not all functions are methods.”

    Example:
    var obj = { m: function(this, x, y) {} };
    var func = obj.m;
    // Both of the following are equivalent
    obj.m(1, 2); // invoke as method
    func(obj, 1, 2); // invoke as function, similar to call()

    This would also “self=this”, because “this” would never appear in non-method functions (e.g. when using them for looping). Those would have one argument less, making them true functions. Does this make sense or am I missing something?

    “Object.getPrototypeOf is in ES5.”
    Right. I would love to additionally have Object.setPrototypeOf() (or __proto__ in all browsers).

  53. @Axel: your Example is not backward compatible, but if you used #(this, x, y) {}, then that is what we arrived at during the last TC39 meeting — except we also want #(x, y) {} (note: no leading this param) to work *and* to inherit lexical |this|.

    Object.setPrototypeOf is not going to happen. Writable __proto__ is a giant pain to implement (must serialize to cycle-check) and it creates all sorts of type-confusion hazards. You may think you want it as a low-level sharp instrument. JS is not that language. Higher-level forms for classes and mixins seem much better and do not involve such sharp edges.

    @Colin: I’m not against IDEs, I simply did not see how Netbeans was relevant. Maybe it is, I don’t know enough still. But consider an IDE with DoctorJS-style smarts: it could figure out that you passed both a string and an array containing a string to getFirstElement, and users could notice and make corrections.

    And DoctorJS could do more, too. In your first example, you had:

    if (Object.prototype.toString.call(param) == “[object Array]“) {
    

    Some custom (to JS) analysis work beyond what is in DoctorJS today could evaluate the truth of this condition based on all the types that flow into getFirstElement. For any that result in a false condition, a flag could be raised. So much more to do in DoctorJS…

    Again, I’m not arguing that static analysis is the same as type checking with type annotations, but we aren’t going to have static types in JS soon or easily. Developers need better tools now. So even if guards show up in ES.next, the bulk of JS will want smarter analysis.

    /be

  54. OK. As long as “this” isn’t bound in non-methods, everything should work out fine. As an aside: I wonder if that couldn’t be retro-fitted into current JS versions, e.g. by not changing “this” if it is assigned null via call() etc.

    I’d watch out for keeping things consistent, though: apply() and call() lead one to the assumption that “this” is always an implicit parameter (in both methods and non-methods). Well, now it actually is, but that will change with sharp-functions.

  55. netbeans is relevant because it purports to have first-class support for javascript, and i wanted to be sure i tried a wide variety of professional tools before claiming that the general state of javascript support in today’s ides is lacking. in my search for javascript production tooling, i tried all the most popular options: webstorm (and idea), komodo, aptana, netbeans, and jsdt (but not visual studio as i’m on a mac). as i described earlier, no current ide could give me accurate code hinting, refactoring, early errors, and documentation generation. i think that’s a problem worth addressing at the language level.

    i’m definitely looking forward to the ide improvements guards would bring, but i fear the static analysis you’re describing is hard enough to get right that real-world ides may never reap its potential benefits. that said, i agree that static analysis is very cool. an ide based on both static analysis and guards-based annotations could be really amazing.

    thank you for seeing this discussion through to the end. i hope my observations from the field will, in some small way, contribute to tc39’s language-design process.

    colin

Leave a Reply to Anthony Mills Cancel reply

Your email address will not be published. Required fields are marked *