On dropping @names

David Herman dherman at mozilla.com
Wed Dec 26 20:36:07 PST 2012

On Dec 26, 2012, at 7:40 PM, Brendan Eich <brendan at mozilla.com> wrote:

>> Many also believe that hoisting is an excellent feature, not a weirdness.
> For functions, I can defend hoisting, although if I had had more time, I might have done a let ... in ... or BCPL'ish equivalent form that groups the recursive bindings. For vars hoisting is pretty much an implementation abstraction leak in JS1 :-P.

I absolutely can defend hoisting. (It's hoisting to *function scope* that's the issue, not hoisting itself.) Here's the rationale:

- JS is dynamically scoped, so having an implicit dummy value isn't a problem for the type system.

- It automatically makes forward references work, so you can:
* order your definitions however it best "tells the story of your code," rather than being forced to topologically sort them by scope dependency
* use (mutual) recursion

- It binds variables without any rightward drift, unlike functional programming languages.

This is such a simple, practical, and elegant win that Scheme goes halfway towards the JS hoisting semantics by having nested definitions:

    (lambda ()                   ;; function() {
      (define (f) (g))           ;;     let f = function() { return g() }
      (define (g) (f))           ;;     let g = function() { return f() }
      (f))                       ;;     return f() }

and last I heard, Racket in fact has gone the rest of the way -- it's moved *towards* the hoisting semantics by allowing definitions (i.e., declarations) and expressions to intermingle:

    (lambda ()                   ;; function () {
      (define (f) (g))           ;;     let f = function() { return g() }
      (printf "hello world~n")   ;;     console.log("hello world");
      (define (g) (f))           ;;     let g = function() { return f() }
      (f))                       ;;     return f() }

Yes, that's right, JS beat those hoity-toity Schemers to it by over a decade! And in fact, the initial binding of a variable in Racket is the #<undefined> value! Sound familiar? :)

To be fair, I haven't kept up with the changes in Racket in recent years, so I don't know if there are some cases where it does a dynamic error instead of returning #<undefined>. But my point here is just that JS isn't alone in doing hoisting. It's actually a very sensible design -- it falls out naturally in a non-lazy, dynamically typed language with any kind of mutually recursive bindings.


More information about the es-discuss mailing list