

Is JavaScript getting worse? - caisah
https://buzzdecafe.github.io/code/2015/01/08/is-javascript-getting-worse/

======
ajanuary
> This means that typeof can now throw
    
    
        >>> def f():
        ...     type(x)
        ...     x = 10
        ...
        >>> f()
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
          File "<stdin>", line 2, in f
        UnboundLocalError: local variable 'x' referenced before assignment
    
        irb(main):010:0> def f()
        irb(main):011:1>     x.class
        irb(main):012:1>     x = 10
        irb(main):013:1> end
        => nil
        irb(main):014:0> f()
        NameError: undefined local variable or method `x' for main:Object
        	from (irb):11:in `f'
        	from (irb):14:in `evaluate'
        	from org/jruby/RubyKernel.java:1101:in `eval'
        	from org/jruby/RubyKernel.java:1501:in `loop'
        	from org/jruby/RubyKernel.java:1264:in `catch'
        	from org/jruby/RubyKernel.java:1264:in `catch'
        	from C:/jruby-1.7.12/bin/jirb_swing:53:in `(root)'
    
        public class Test {
        	public void f() {
        		boolean isInt = x instanceof Integer;
        		Integer x = 10;
        	}
        }
        >javac Test.java
        Test.java:3: error: cannot find symbol
                        boolean isInt = x instanceof Integer;
                                        ^
          symbol:   variable x
          location: class Test
        1 error
    

Oh no, it's now in line with just about every other lexically scoped language.

~~~
toolz
Except, now you have one that you expect and the other you don't. This doesn't
make the language anything but more inconsistent. I prefer consistency inside
a language far more than familiarity between languages.

~~~
ajanuary
Remember, it's not the function that's throwing the exception, it's the
runtime. In order to make it not throw an exception, we'd have to special case
typeof to make the runtime behave differently just for it. /That/ is
inconsistent.

~~~
erkl
This throws an error:

    
    
        typeof x; // throws an error
        let x = 1;
    

This doesn't:

    
    
        typeof x; // returns "undefined"
        var x = 1;
    

While using variables before declaring them is bad practice, I think it's fair
to argue that this behaviour is inconsistent.

~~~
ajanuary

        let x = 0;
        function typeof_wrapper(y) { return typeof y; }
    
        (function() {
            typeof_wrapper(x); // throws an error
            let x = 1;
        })();
    
        (function() {
            typeof_wrapper(x); // returns "undefined"
            var x = 1;
        })();
    

Again, typeof isn't throwing the error, the runtime is, because a variable
declared with "let" is being referenced before the declaration. It's not
inconsistent, it's one of the main points of "let".

You're essentially arguing that a feature added because the old behaviour was
undesirable is inconsistent because it's not exhibiting the old undesirable
behaviour.

------
mmahemoff
Whoop-dee-freakin’-doo???

Sure there's a zillion frameworks and patterns for shoehorning OO into
JavaScript. But the point is, ES6, is standardising it. That means libraries
going forward will assume this is the way it's done and build on top of it. It
gives them more expressive power and better inter-op.

Also, default params are long overdue but I'm not sure that makes them "so
what". I'd rather avoid mangling the arguments array in some awkward if
statements.

Bottom line, there's a lot of things about JS that aren't great, but it's what
all the browsers actually run so we're kind of stuck with it and any
improvements are welcome.

(Even if you use something like ClojureScript or CoffeeScript, JS is still the
target language and the runtime. Improvements and standardisation matter.)

~~~
JackMorgan
I think he means OO adds unnecessary complexity, hence the "bad bet".

~~~
phpnode
ugh, really sick of this meme. OO is a tool in the box, it's useful a lot of
the time, sometimes it isn't. The reason ES6 introduces the `class` keyword is
that people _are doing that already_.

~~~
rtpg
Just because a tool is in a box doesn't mean its useful. Considering the
relative cleanliness, refactorability of code coming from the functional world
(see Haskell and the like), it is debatable whether OOP is a "good" style to
develop at all!

If I look at most of the libraries commonly used in production JS, pretty much
none of them apply classic OOP principles.

~~~
Demiurge
Just because it's possible to write a program without a tool, doesn't mean
it's more productive. Functional programming doesn't even directly oppose OOP,
it opposes imperative. It is absolute basics of CS that there is data and
logic and many problems are easy to reason about as logic modifying data
(state). OOP is a tool that allows to encapsulate some data and logic that go
together. Functional programming doesn't allow you much more than that. Just
because you may (or may not) end up decomposing a program into the smallest
units of logic, doesn't mean you've done something useful, or clean.

~~~
tel
Without backing a horse exactly:

* FP as I understand it doesn't much oppose imperative or even most of OO. It opposes non-composability using discipline from pure/total languages.

* Even if you want to encapsulate data and logic together like you suggest, you only need to buy 10% of OO to get that (ADTs, existential types, modules, what-have-you do it fine if not far better). The remaining 90% may be a waste or actively harmful.

* FP, as I practice it, means using the simplest possible thing in a world where simple things compose nicely. This may end up being some kind of full-scale OO, I'm eager to try to find places where it does, but so far it hasn't ever.

As a meta note, "my kind of FP" is the Haskell type of purity and other such
nonsense.

~~~
Demiurge
>FP as I understand it doesn't much oppose imperative or even most of OO

I think this is definitely true, if you take 'oppose' as 'incompatible'. If we
look the design concept and plot them, there will be axes, and I think this is
what we'll see.

>* Even if you want to encapsulate data and logic together like you suggest,
you only need to buy 10% of OO to get that

That's also true, but it's not an argument against the seed of usefulness of
the 10%. We have kitchen sinks because people pick and choose features at
will.

>* FP, as I practice it, means using the simplest possible thing in a world
where simple things compose nicely.

Same here, and everyone should practice this when they look at any function,
or object method, trying to make it as atomic as possible.

> This may end up being some kind of full-scale OO

I don't think it's any more useful shoehorning a program into "everything
objects" more than "everything is functions".

My point was to say that the core goal of OOP is a great one, it simply
codifies something that people have done in C when they put structs and
procedures in the same header file. Let's not throw the baby out with the bath
water.

~~~
tel
My point is merely that the benefits of are gained using essentially nothing
more than real ADTs and yet are almost always bundled with harmful other
things in practice. FP can include these core ideas nicely, much like how
monads include imperative ideas nicely, by focusing on what composes well.

~~~
Demiurge
As long as I'm not using C++ I don't feel harmed by any OOP features I don't
have to use. I don't feel being restricted. I'm curious, what are these
commonly harmful things you're talking about?

On the other hand, most FP languages I tried feel very restrictive. The only
language that is inclusive of all good ideas, seems to be F#.

~~~
tel
I'm not a big fan of languages allowing mutability anywhere. You can use it,
sure, but advertise in big bold letters as to where. This plays out repeatedly
for any side effect.

There are some OO systems with effect types I suppose. Those would be
interesting.

There are also OO systems that don't have mutability. But I think that it you
remove mutation, all that class nonsense, and are careful with subtyping
(since it breaks things badly often) then you basically have (badly typed) ML
modules with open recursion.

The open recursion (a.k.a. late binding) bit is probably the most interesting
thing but you can play with it in Typed LC just fine. It's just another form
of recursion.

~~~
klibertp
> There are some OO systems with effect types I suppose. Those would be
> interesting.

Nim has something IIRC, but I didn't look into it yet.

------
ahoge
> _Classes. Whoop-dee-freakin’-doo._

There are dozens of ways to do classes/inheritance. Standardizing this means
better tooling, documentation, and interoperability.

> _Default parameters. By itself, this is another “so what” feature._

It's extremely useful in conjunction with named parameters. Named parameters
are so much better than "option" objects.

If it's right in the function's signature, you can see right away how this
thing is supposed to be used. Since it's declarative, it can be also picked up
by your editor, too.

> _let considered harmful_

No, it's not. It makes variable declaration work like everywhere else.
Function scope is the super weird anomaly.

------
cronin101
> let considered harmful

Did people actually use the side-effect var-hoisting intentionally within
their code?

Pretty much any JS style-guide worth its salt suggests manually moving var
declarations to the top of scope since it's nice to know ahead-of-time which
indicators of state you should be keeping an eye on.

The idea of inspecting a variable that is later-on defined with let seems
baffling to me. I can't think of any reason why you would want to do this.

~~~
kosinus
I tend to declare vars with their context. Especially in functions that are
(unfortunately) longer than usual, having all vars at the top makes for a
mess.

But I don't think the `let` syntax is going to be a problem for people who
write plain JavaScript. It's more likely to it might become a problem for
languages that compile to JavaScript. (For example, soak operators in
CoffeeScript.)

------
basicallydan
> let considered harmful: The problem with let is that it is not “hoisted” to
> the top of its block, as var is with its containing function.

Interesting point, but I disagree. I think that the lack of hoisting is one of
the _benefits_ of `let`. It works in a different way, which is more in line
with other languages. Sometimes, you don't want a bunch of variables at the
top of your function. Many functions do not need to be executed in the way
that hoisting makes easier.

By the way, quoting Betteridge’s law of headlines at the beginning of your
article whose headline is a question does not mean you get a free pass of
using such a headline ;)

------
gamesbrainiac
This is one of the few posts about javascript that I've encountered that sees
hoisting as a good thing. I like `let`.

~~~
quarterto
Have you read the linked article about typeof? Normally, it's safe to do
typeof possiblyUndeclaredVariable, but if later in the function you do let
possiblyUndeclaredVariable it starts the scope defined as "uninitialized",
which causes typeof to throw.

~~~
umurkontaci
Well that's perfectly normal in a properly scoped language.

~~~
virmundi
What you have to keep in mind is that JavaScript is not "a properly scoped
language". Pretending that it is will cause you to miss key aspects of how the
tool works. This helps no one, including you. Please, for as crappy as the
language might feel, approach the language on its own terms.

~~~
umurkontaci
Function scopes and variable hoisting was not the best parts of JS anyway.
_let_ brings lexical scope into the game, which I think is a great progress.
And you cannot expect a variable to be defined outside of its _lexical_ scope.
That's not any different that trying to access a variable outside of a
function that is defined in.

If people were abusing variable hoisting in some way, they can continue to do
so, by not using `let`.

~~~
makomk
If I'm following the blog post correctly, let does have effects outside of its
lexical scope: it effectively makes the variable _even more undefined than a
totally non-existent variable_. That is, the following code will not throw
anything:

    
    
        // x has not been defined or initialized anywhere
        console.log(typeof x)
    

However, if you add a let statement after it like so:

    
    
        // x has not been defined or initialized here
        console.log(typeof x)
        let x = "foo";
    

typeof will fail with an exception, because the let statement changes x from
an undefined variable to a new state that isn't even undefined anymore.

~~~
ajanuary
It's not having an effect outside of its lexical scope. You introduce the let
into the same lexical scope as the console.log.

It will also throw an exception every single time. So unless you're adding a
variable, and then never testing the code path that hits the new line, you're
probably going to catch it pretty early.

~~~
TeMPOraL
Well, but doesn't let itself create a new implicit scope, from the point of
the let statement to the end of the scope the statement is in? It seems to me
that here let is indeed acting on things outside of lets own implicit lexical
scope.

~~~
ajanuary
They are in the same lexical scope.

Inside a lexical scope, all matching variable names refer to the same
variable. Because they are in the same lexical scope, all instances of the
variable name "x" refer to the same variable.

Separate (but related) to this is the concept of where it is valid to
dereference variables (this is where my knowledge breaks down - is there an
accepted term for this?). Javascript says that for variables declared with
"var", it is always valid to dereference them, but it might dereference to
"undefined". For variables declared with "let", it is only valid to
dereference variables in the lexical scope. In addition to this, it defines a
"temporal dead zone", which covers the span between the start of the lexical
scope and the "let" declaration. This isn't a novel thing - other languages do
it, though they may use different terminology.

It's this "temporal dead zone" that you seem to be referring to when you say
it's creating a new implicit scope.

It's still impossible for a let declaration to affect anything outside of its
lexical scope. If it's shadowing a variable name in a parent scope, what it
can do is stop variable names earlier in the scope from referring to the
parent scope and make it refer to the variable in inner scope. This may seem
like a problem, but most other languages will do the same thing (C++, Java,
C#, Python, Ruby etc.) and I've never seen it be much of an issue.

------
phpnode
ES6 is fantastic and usable today using the excellent 6to5 [0]. I disagree
with all of the author's points, I think these are great features.

[0] [https://github.com/6to5/6to5](https://github.com/6to5/6to5)

------
collyw
Whats his problem with classes?

I find them a whole lot easier to reason about than prototype based
inheritance. (But then I have more experience with class based languages).

~~~
vim-guru
Classes adds complexity and reduces composability. With OO you end up with a
soup of inheritence and patterns. Functions are way easier to compose and
reason about.

~~~
lgunsch
> _Classes adds complexity and reduces composability._

I could easily argue the opposite. Classes reduce complexity, and increase
composability. Classes allow you to group methods in a cohesive manner into a
single entity. For example, a class named Queue, List, or Vector would imply
to the programmer what it does without them reading any code at all.

> _With OO you end up with a soup of inheritence and patterns._

Just because programmers don't make clean code, doesn't mean the whole idea is
somehow lesser then just using functions. Having a hodge-podge of 2000 line
functions in one massive file is just as evil as "dirty" OOP code. At least
classes encourage you to group your functions in some hopefully sane manner.

> _Functions are way easier to compose and reason about._

Functions are easy to compose and reason about, however, that does not mean
that classes and OOP are not as easy. Each have their own clear
costs/benefits. OOP, and procedural programming, should both be in a
programmers toolbox.f

~~~
buzzdecafe
(article author here) I very much agree with this point of view. The most
important concern is a developer who thinks and writes clearly, irrespective
of style or programming paradigm.

That said, I find FP easier to reason about and comprehend, but that is just
_one man 's opinion_. I also find it more _fun_.

The "bad bet" I was referring to was that you can make JS impersonate Java.
That strikes me as a losing proposition. Sugaring up prototype inheritance to
look like class-based inheritance just muddies the water.

------
robinduckett
I like `let`. It means you have to be implicit and actually be aware of what
you're doing.

~~~
ajanuary
That part of the article isn't particularly clear. The intended behavior of
let is great. The unintended behavior - that it can cause typeof to fail - is
bad.

~~~
taf2
It seems to me typeof in the described case should return undefined.

~~~
ajanuary
typeof is never being invoked, so it can't return anything.

The rules of let are that any reference to the variable before the let
declaration (the "temporal dead zone"), it's a reference error. So the
reference error is being thrown before typeof is invoked.

------
Hypx
JavaScript is definitely getting better. The only question is whether they are
changing too much too fast. There'll be a lot of headscratchers when running
into unfamiliar ES6 code.

------
cwmma
So classes in ES6 are something I have very mixed feelings about, on one hand
all that is being added is syntactic sugar for what is already being done, I'd
like to repeat that ES6 classes add NOTHING that isn't already being done and
all it really does is pave the cowpath that is being used in places like node

Currently:

    
    
        JSONParseStream.prototype = Object.create(Transform.prototype);
        function JSONParseStream(options) {
    
        Transform.call(this, options);
            ...
        }
       
        JSONParseStream.prototype = Object.create(Transform.prototype);
    
        Object.defineProperty(JSONParseStream.prototype, 'constructor', {
            value: JSONParseStream,
            enumerable: false,
            writable: true,
            configurable: true
        }
    
        JSONParseStream.prototype._transform = function(chunk, encoding, cb) {
            ...
        }
    

With ES6:

    
    
        class JSONParseStream extends Transform {
            constructor(options) {
                super(options);
                ...
            },
            _transform(chunk, encoding, cb) {
                ...
            }
        }
    

That being said, the fact that previously you could not simply use the word
class meant that despite efforts of people unused to the language (let me tell
you how many half assed class libraries i've seen in code written by people
who mainly code in python or java but have some web code as well), unnecessary
inheritance tends to be avoided. The lack of a class keyword tends to get
javascript writers to avoid inheritance for things best served by mix-ins or
object literals, or whatever. I predict that adding the class keyword, while
saving me some time will also cause an uptick to unnecessarily convoluted
inheritance patterns as new users find they can implement all of their design
patterns verbatim without thinking if they really need the ajax method, the
json parser, and the url builder to all be in objects that inherit from each
other.

------
arcatek
I'm sorry, but I really see no point in this article.

Do you realize that if the optional arguments were not included in
Function#length, you'd just be saying "They are not reflected in the
function’s length property. C'mon, man" instead ?

Also, I don't understand Whoop-dee-freakin’-doo.

~~~
quarterto
"They are not reflected in the function’s length property" is exactly what the
article _does_ say.

    
    
      Whoop-dee-freakin’-doo
    

[http://en.wiktionary.org/wiki/whoop-de-
doo](http://en.wiktionary.org/wiki/whoop-de-doo)
[http://en.wikipedia.org/wiki/Tmesis](http://en.wikipedia.org/wiki/Tmesis)

~~~
arcatek
My bad, I misread. However, my point is still the same: there was two equally
valid choices. Should the other path had been prefered, the author would now
be complaining about it too. I don't find it very constructive.

As for Whoop-dee-freakin’-doo, no, it don't think it means anything in the
context. That's not an argument, and the following sentence isn't either.
Maybe I don't understand because english isn't my native language, tho.

~~~
chriswarbo
From my understanding, the author was complaining about the way default
arguments mess up implementations of currying. If default arguments were
included in the .length, then all existing currying implementations would
carry on working with no modifications, they would just ignore the default
values and require that we pass in values explicitly.

Let's say we have these functions:

    
    
        function getElem(arr, index=0) { return arr[index]; }
        var curried = curry(getElem);
    

In a hypothetical world where default arguments _are_ included in the length,
we would have to supply them explicitly. That's no big deal though, it's just
a potential inconvenience:

    
    
        // Hypothetical, "better" semantics
        var arr = ["a", "b", "c"];
        console.log(curried(arr));     // partially-applied function, *not* "a"
        console.log(curried(arr, 0));  // "a"
        console.log(curried(arr, 1));  // "b"
    

The ES6 semantics is worse because we _cannot_ provide a non-default argument
to a curried function:

    
    
        // Actual, "worse" semantics
        console.log(curried(arr));     // "a"
        console.log(curried(arr, 0));  // Error: attempts to run `"a"(0)`, but "a" is not a function
        console.log(curried(arr, 1));  // Error: attempts to run `"a"(1)`, but "a" is not a function
    

The reason this is particularly unfortunate is that one major use-case of
currying is _to provide default arguments_! In other words, by adding default
arguments to the language in this way, ES6 is breaking an _existing_ mechanism
for default arguments!

I would argue that currying is actually more general than default arguments,
so if it's a choice between one or the other, they should have added currying
to the language instead of default arguments.

They're not quite comparable, but you can think of currying as supplying
default arguments "dynamically" (at the call site), whereas regular default
arguments are supplied "statically" (at the definition site). For example:

    
    
        // Give "y" a default value for everyone
        var defaultMultiply = function (x, y=2) { return x * y; };
    
        console.log(defaultMultiply(5, 7));  // 35
        console.log(defaultMultiply(5));     // 10
    
        // Don't commit to any defaults yet
        var curryMultiply = curry(function(x, y) { return x * y; });
    
        console.log(curryMultiply(5, 7));  // 35
    
        // Give "x" a default value, to act like defaultMultiply
        var double = curryMultiply(2);
    
        console.log(double(5));  // 10
    
        // Give "x" a different default, which we can't do with defaultMultiply
        var triple = curryMultiply(3);
        console.log(triple(5));  // 15
    

I've implemented and used currying in JS, PHP and Python, and I found JS the
most pleasant, specifically because it didn't have default values.

~~~
CrossEye
Very well said. I work on the Ramda
([http://ramdajs.com/](http://ramdajs.com/)) FP JS library with the OP. Ramda
is heavily invested in currying, and I don't yet have a clue how we'll deal
with default parameters. At first guess, we'll simply have to explain that we
can't curry such functions. Such a shame!

------
Kiro
I don't understand the benefit of fat arrow. I actually think it decreases the
readability. What does lexical "this" binding mean?

~~~
brazzy
Fat arrow notation massively increases readibility by reducing the boilerplate
verbosity of function definitions. Lexical "this" binding means that the
"this" of the enclosing scope is reused; Arrow-defined functions do not get
their own "this" when they are called.

~~~
Kiro
Thanks but massively? function() {} vs () => {}?

~~~
secoif
Also removes return statement for 1 liners. Reduces clutter of anon functions,
especially when you're chaining a bunch together

e.g.

ES5

    
    
        users.map(function(user) {
          return user.name
        }).filter(function(name) {
          return name.length < 30
        }).sort(function(a, b) {
          return a.length - b.length
        })
    

vs

ES6

    
    
        users
        .map(user => user.name)
        .filter(name => name.length < 30)
        .sort((a, b) => a.length - b.length)
    

Contrived, but typical example.

------
Mithaldu
Why didn't they call "spread" by the same name it's called virtually
everywhere else, "flatten"?

~~~
collyw
I hate this. Why can languages try and standardize on things to at least some
extent.

else if (JavaScript) elsif (Perl) elif (Python)

There is no good reason that these are different.

~~~
CrossEye
Yes, and those damned Italians still refuse to switch to English too!

------
JacobEdelman
Eh, a lot of the additions are improvements, a few are not, a very small few
introduce some annoying features. It's an improvement, maybe not perfect, but
still a big improvement. Disclaimer: I like JavaScript.

------
filipncs
_Best of all, proper tail calls_

When are proper tail calls expected to show up? Looks pretty bleak.

[http://kangax.github.io/compat-table/es6/](http://kangax.github.io/compat-
table/es6/)

------
forthefuture
I'm more interested by how many people still seem to be using typeof instead
of toString. What are you doing for array checking since typeof array ===
"object", !!x[0]?

~~~
noiv
There is also Array.isArray()

------
jerryluc
Betteridge's law[1] strikes again.

[1]
[http://en.wikipedia.org/wiki/Betteridge%27s_law_of_headlines](http://en.wikipedia.org/wiki/Betteridge%27s_law_of_headlines)

~~~
collypops
That just gave you up as someone who didn't follow the link.

~~~
arcatek
To be fair, the article itself ends with "No"

> So is Javascript getting worse? Well … no.

