Hacker News new | ask | show | jobs
by antonvs 2076 days ago
So, there are excuses for Javascript's terribleness, but that doesn't stop it from being objectively terrible.
3 comments

Sad to see the word "objective" become the next bullshit intensifier because people can't separate their own subjective opinions from the realm of verifiable fact.

"Terribleness" isn't an objective property.

You can compare Javascript to other languages and note that many of its notorious problems have no rational justification, and are unnecessary. That's what I call objectively terrible.
Yeah, that's not objective. Sorry.
What do you think "objective" means?

The comparative approach I mentioned can be used to eliminate personal feelings about such issues - not in all cases (types might be an example), but certainly in some.

Many users of Javascript, including myself, recognize that it has many weaknesses. There's even a book that acknowledges this in its title: "Javascript: The Good Parts."

Denying this seems to be denying objective reality.

You may be confusing "objective" with "universal," thinking that I'm claiming some unsituated universal truth. But that's not the case. Any statement is only ever true within some context - the language that defines it, the semantics of the statement, the premises that it assumes.

In this case, there is a shared context that crosses programming languages, that allows us in at least some cases to draw objective conclusion about programming language features. "The bad parts" implied by Crockford's title includes many such features. We can examine them and conclude that while they might have some historical rationale, that they are not good features for a programming language to have.

In many cases this conclusion is possible because there's simply no good justification - the title of this post is an example. Having all numbers be floating point has many negative consequences and no significant positive ones - the only reason for it is historical. Such features end up having consequences, such as on the design of hardware like ARM chips. That is an objectively terrible outcome.

You can of course quibble with such a statement, based on a rigid application of a simplistic set of definitions. But you'd do better to try to understand the truth conveyed by such a statement.

What do YOU think "objective" means?

None of the properties you're talking about are objective. Objective doesn't mean Crockford wrote a book about it or "lots of people agree with me".

Objective means factual. You're putting the word "objective" in front of your own and others opinions to arrogate the credibility of objectivity onto statements that are not based in observation of material reality.

More people holding an opinion doesn't make it a fact. "Terribleness" or "justifiableness" are not matters of fact, they are both matters of opinion.

Do you understand? You keep repeating your opinion and then using the word "objective" to claim that your opinion is fact. You think I am disagreeing with your opinion, rather I am disagreeing with you stating your opinion is a fact. No matter how many people agree with you it will never be a fact, it will always be an opinion because "terribleness" is not a matter of fact! "Terribleness" is the result of a value judgement.

There are no such things as "objective conclusions", objectivity is not a manner of reasoning. You're looking for something more like "observations", "measurements", hard facts.. none of which apply to "terribleness" because it can't be materially observed--only judged.

"Objectively" isn't an intensifier unless used in the form "Objectively [something that isn't objective]." Why would actual facts need to be intensified? What kind of insane argument would anyone have where facts and opinions are compared directly?

I know it sounds stronger to say your opinions are facts but it is okay to have opinions. Just remember that the difference between opinions and facts is a difference of kind rather than a difference of degree. You can argue an opinion, you can attempt to persuade me to your way of thinking if you show your reasoning.

You can just look up some dictionary definitions, like "not influenced by personal feelings or opinions in considering and representing facts." I've explained how that applies in this case - we can use comparative analysis to draw factual conclusions.

Focusing on the specific word "terrible" is a bit silly. Sure, it's hyperbolic, but I used it as a way to capture the idea that Javascript has many features that are comparatively worse at achieving their goals than equivalent features in many other languages. This is something that can be analyzed and measured, producing facts.

Crockford's book title is simply an example of how even a strong advocate of Javascript recognizes its weaknesses. You may not understand how it's possible for it to objectively have weaknesses, but that doesn't mean it doesn't. In this case an objectively bad feature would be one that has negative consequences for programmers, and can be replaced by a features that can achieve the same goals more effectively, without those negative consequences.

If there's anyone who'll argue in favor of such features on technical rather than historical grounds, then it would certainly undermine the objectivity claim. But the point is that there are (mis)features in Javascript which no-one defends on technical grounds. That is a reflection of underlying objective facts.

I'm also not making some sort of ad populum argument. As I pointed out, any claim of objective fact has to be made in some context that provides it with semantics. In some languages, the expression "1"+"1" is a type error, in others it produces "11". Both of those outcomes are objective facts in some context. What your objection really amounts to is saying that there's no semantic context in which my claim could be true. That's clearly not the case.

Perhaps a different example would help: people don't write programs any more by toggling binary codes into machines via switches. That's because we've come up with approaches that are objectively more effective. We can factually measure the improvements in question. The features I was referring to fall into the same category.

I'm going to repeat the closing from my last comment, because you're still doing the same thing:

You can of course quibble with such claims, based on a rigid application of a simplistic set of definitions. But you'd do better to try to understand the truth conveyed by the claims, and engage with that.

Normally JS devs don't really encounter these notorious problems, for many years now.
You sound like a complete novice, funboy or the one who knows only JS. There are many issues, it is possible not to touch or to work around them, TS and flow helps. JS solved just a few — 'use strict', strict comparison, arrow functions, string literals. Core problems still there — implicit types, prototype declarations, number is float, toString/inspect division, typeof null. Every javascript programmer has to walk that way of embarrassment.
Lol.

I've been programming for a decade in many languages including assembly, C#, Rust, Lisp, Prolog, F# and more, focusing on JS in the last 5 years.

Virtually no one writes plain JavaScript, most people including me write TypeScript, but Babel with extensions is normally used. Your reply exhibits your ignorance of the JS world.

I occasionally write JavaScript since 2007, experiment a lot last 5 years, red through ES5 specification several times. I've worked as C++, PHP, Python, Ruby developer. Experimented with a few languages.

"JS" instead of "TypeScript" brings confusion. TS solves some issues and I've mentioned it, still

    typeof null
    //"object"
Template literals interpolation helps but if string (not literal string) slips by it is a mess

    1 - "2"
    //-1
    1 + "2"
    //"12"
Check out another comment [1], Object, Function, etc defined as constructor. It is not solved by "class", it is still a function with a bit of sugar:

    class Foo {}
    Foo instanceof Function
    //true
Globals with a few exceptions defined as constructors, DOM elements defined as constructors, inheritance defined as constructors

    class Bar extends Foo {}
You can internalize how it works and there are some good explanations [2] but design is error prone and terrible.

[1] https://news.ycombinator.com/item?id=24815922

[2] https://yehudakatz.com/2011/08/12/understanding-prototypes-i...

> Virtually no one writes plain JavaScript

Which is because plain Javascript is objectively terrible, as I pointed out.

And that's your opinion. I find javascript quite enjoyable and easy to use, without producing errors. YMMV.
JavaScript has good parts, I write it a lot. But it is ignorant to close eyes on its warts

    1 + '2'
    1 - '2'
    Number.MAX_SAFE_INTEGER + 2
and entire WAT series, stems from "don't raise" ethos. JavaScript exposes constructor instead of prototype that messed up a lot, in Ruby terms

    Object.alias_method :__proto__, :class
    Object = Object.instance_method(:initialize)
    Class = Class.instance_method(:initialize)
    Class.__proto__.alias_method :prototype, :owner
    new = ->(constructor) { constructor.owner.new }
    
    Person = Class.prototype.new do
      def initialize
      end
    end.instance_method(:initialize)
    
    def Person.foo
      'foo'
    end
    puts Person.foo

    john = new.call Person
    
    def john.bar
      'bar'
    end
    puts john.bar
    
    def (Person.prototype).baz
      'baz'
    end
    puts john.__proto__.baz
Does anyone wants to adopt this feature in their language?
Operator overloading can lead to ambiguities in dynamic languages. Ruby, python, and any number of other languages have it much worse because they can be overloaded any way you want while JS overloads are (currently at least) set in stone by the language.

If you could only choose one number type, would it be floats or ints? Crockford would say decimal, but the rest of use using commodity hardware would choose floats every time. It's not the language implement's fault someone doesn't understand IEEE 854. This max safe integer issue exists in ALL languages that use IEEE 854. In any case, BigInt is already in browsers and will be added to the spec shortly.

Perl and Lua have separate arithmetic and concatenation operators

    1 + "2"
    #3
    1 - "2"
    #-1
    1 . "2"  | 1 ..  "2"
    #"12"
    1 . "-2" | 1 .. "-2"
    #"1-2"
Python and Ruby throw exception, use explicit type conversion and string interpolation

    1 + '2'
    Traceback (most recent call last):
    TypeError (String can't be coerced into Integer)
    1 + '2'.to_i
    #3
    "1#{'2'}"
    #"12"
Hardly any language is perfect, I have not encounter much of operator overload in ruby (nokogiri?) but I believe C++ got it bad.

One number type in Lua:

    > 0 | 0xffffffff
    4294967295
    > 0 | 0xffffffffffffffff
    -1
    > 0 | 0x7fffffffffffffff
    9223372036854775807
limited but better than JavaScript:

    0 | 0xffffffff
    //-1
BigInt is an improvement

    0n | 0xffffffffn
    //4294967295n
    0xffffffffffffffffn
    18446744073709551615n
it is strict

    1n - "2"
    1 + 1n
    Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
works nice with string interpolation:

    `${1n}`
    "1"
Numbers is a sane example. One can argue it was for good. How about `{} + []`? I believe I can disable this part in JavaScript engine and no one would notice. And misleading `object[key]` where it calls toString, sure I have not tried that in a decade but it is stupid. UTF-16:

    ""[1] # there were emoji
    //"�"
You've said nothing about constructor oriented programming. Unique feature, I have not heard any other language adopted it yet. The post you've replied contents sketch for Ruby. Actually I've got it wrong — every JavaScript function is a closure (Ruby method is not closure) and Prototype method was a class method (not instance method), fixed but ugly:

    def function(&block)
      Class.prototype.new.tap do |c|
        c.define_method(:initialize, block)
      end.instance_method(:initialize)
    end

    def function_(object, name, &block)
      object.class_eval do
        define_method(name, &block)
      end
    end

    Person = function { |name|
      @name = name
    }
    function_(Person.prototype, :name_) {
      @name
    }

    john = new.call Person, 'john'
    puts john.__proto__ == Person.prototype
    puts john.name_

    def function__(object, name, &block)
      object.singleton_class.class_eval do
        define_method(name, &block)
      end
    end

    function__(john, :name__) {
      @name
    }
    puts john.name__
By the way, you can say "Yes, I know JavaScript has some problems". It is not a secret, everyone knows.
I completely agree that separate operators area MUST for dynamic languages.

Lua allows operator overloading with metatables as do ruby and Python with classes

http://lua-users.org/wiki/MetatableEvents

https://docs.python.org/3/reference/datamodel.html#emulating...

https://www.ruby-lang.org/en/documentation/faq/7/

> One number type in Lua:

Not quite true. Lua had only 64-bit floats like JS until version 5.3 and the blazing fast LuaJIT still only has floats. Well, to be honest, it has hidden 32-bit integers for sake of bitwise operations just like JS (well, JS uses 31-bits with a tag bit which is probably a lot faster).

> How about `{} + []`? I believe I can disable this part in JavaScript engine and no one would notice.

That's very simple. {} at the beginning of a line is an empty block rather than an object (yay C). "Disabling" that would break the entire language.

> UTF-16

UCS-2 actually. Back in those days, Unicode was barely a standard and that in name only. Java did/does use UCS-2 and JS for marketing reasons was demanded to look like Java. I don't want to go into this topic, but python, PHP, ruby, C/C++, Java, C#, and so on all have a long history not at all compatible with UTF-8.

> You've said nothing about constructor oriented programming. Unique feature, I have not heard any other language adopted it yet.

I'll give you that JS prototypal inheritance is rather complex due to them trying to pretend it's Java classes. Once again though, the deep parts of both Python and Ruby classes are probably more difficult to explain. Lua's metatables are very easy to understand on the surface, but because there's no standard inheritance baked in, every project has their own slightly different implementation with it's own footguns.

Closures are almost always preferred over classes in modern JS. Likewise, composition is preferred over inheritance and the use of prototype chains while not necessarily code smell, does bear careful consideration.

If someone insists on using deep inheritance techniques, they certainly shouldn't be using class syntax as it adds yet another set of abstractions on top. Object.create() and inheriting from `null` solves a ton of issues.

> By the way, you can say "Yes, I know JavaScript has some problems". It is not a secret, everyone knows.

I'd say if you take the top 20 languages on the tiobe index, it sits in the middle of the pack with regard to warts and weirdness. Maybe people are just attracted to weird languages.

I have no grudge against operator overloading when done consciously — complex numbers, matrix multiplication. I've tried to implement JavaScript arithmetic in Ruby, failed so far.

Sorry, I had to be clear, in Lua "one number type" means float, my bad. I meant Lua 5.3 integer still works like JavaScript Number. In the end we have to know about ToInteger, ToInt32, ToUint32, Number.MAX_SAFE_INTEGER [1]. It is not one number type but encoding of several number types, union.

Prior to 5.3 and in LuaJIT it has different limitations

    > print(string.format("%18.0f",9007199254740991 + 1))
      9007199254740992
    > print(string.format("%18.0f",9007199254740991 + 2))
      9007199254740992
they have extended original "one number type" without change of the interface. In any case both versions do not convert to BigNum like Ruby.

    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    => 115792089237316195423570985008687907853269984665640564039457584007913129639935
Ruby unified Fixnum, Integer and BigNum as Integer in 2.4. Can't see benefits of Number/BigInt against Float/Integer. I'd rather have 3.6f literal.

Yes, I know how WAT works. I meant ToPrimitive [2]

    [] * {}
    //NaN
I've disabled this code in Firefox, have not done extensive testing but looks like no one depends on it. We infer types with TypeScript and flow but VM already knows it, it can report such cases without external tools. I think of it as extension of Firefox Developer edition — lint in the browser.

Object.prototype.toString is not as useful as Ruby, Python

    class Foo {}
    `${new Foo}`
    //"[object Object]"

    class Foo end
    Foo.new
    #=> #<Foo:0x0000560b7a10df20>

    >>> class Foo:
    ...     pass
    >>> Foo()
    <__main__.Foo object at 0x7fb53aecf1f0>

And there is no separate inspect/__repr__

    Date.today
    #=> #<Date: 2020-10-20 ((2459143j,0s,0n),+0s,2299161j)>
    Date.today.to_s
    #=> "2020-10-20"
> UCS-2 actually

Oh, DOM UTF-16 string broken by UCS-2 JavaScript function. I understand it is not easy to fix, Ruby fixed in 1.9, Python in 3.0, new languages (Rust, Elixir) come with UTF-8. Microsoft Windows has code pages, UCS-2, UTF-16.

Maybe Python way? b"binary", u"utf-8" (but together, not python fiasco), ruby has "# Encoding: utf-8", transformation tools can mark "b" or "u" all unspecified strings.

> Once again though, the deep parts of both Python and Ruby classes are probably more difficult to explain.

No, every Ruby object contains variables and has a link to a class which defines instance methods, we call it singleton_class

    foo = Object.new
    foo.class == Object
    #=> true
    foo.singleton_class == Object
    #=> false

    def foo.bar
    end
    foo.singleton_class.instance_method(:bar)
    #=> #<UnboundMethod: #<Class:#<Object:0x000055cb33808e10>>#bar() (irb):7>
There is ancestors chain

    Object.ancestors
    #=> [Object, Kernel, BasicObject]
There is a bit of syntactic sugar

    Foo = Class.new
There are few revelations with main (method defined in Object)

    def baz
    end
    Object.instance_method(:baz)
    => #<UnboundMethod: Object#baz() (irb):19>
Nothing like audible "click" I had when understood that "function" is a "constructor"

    constructor Foo {}
    // you can call me as function too
that unlike any other language [[Prototype]] is hidden. I've red through ES5 to be sure there are no hidden traps left.

Every JavaScript programmer has to go through this list either beforehand or by experience. I do not want to undermine TC39 effort — arrow functions, string interpolation in template literals, strict BigInt, Object.create — these are great advancement. I don't feel same way for "class", underlying weirdness is still there.

Make [[Prototype]] visible

    Object = Object.prototype
    Function = Function.prototype
now it is easy to reason about

    typeof Object 
    //"object"

    Foo = class {}.prototype         // redefine with sweetjs macro
    Bar = class extends Foo.constructor {}.prototype

    new Foo.constructor              // redefine with sweetjs macro

    Object.constructor.create(Bar)   // redefine as Reflect.create
once redefined:

    Foo = class {}
    Bar = class extends Foo {}
    new Foo
    Reflect.create(Bar)
I've shown it in another comment [3].

Languages are weird, there are a lot of C++ developers, I've been there, no way to know all dark corners. Pythons ideology hurts. Java took EE way. C# was tied to Microsoft. C K&R is beautiful, hard to write safe, packs a lot in the code. PHP has its bag of problems. SQL is not composable, CTE helps. Go ideology. Ruby — performance. And JavaScript because browser, not bad when know and avoid skeletons in the shelf.

Lua metatables looked like a proxy/method_missing for me.

[1] https://www.ecma-international.org/ecma-262/5.1/#sec-9.5

[2] https://www.ecma-international.org/ecma-262/5.1/#sec-9.1

[3] https://news.ycombinator.com/item?id=24815922

Sure, until you parachute into a code base where several generations of contractors added features that communicate over a shared global object. This is bad per-se, but becomes worse when your language allows one to add fields on the fly and you end up with this container full of similar fields because eventually nobody knows exactly what’s in the object any more...
And that seems pretty simple to fix. "The same level of awareness that created a problem cannot be used to fix the problem" - and maybe that's why they hired you, to fix these problems. I've been that guy before. What was really fun was the codebase I had to fix that used mexican slang words as variable names, and I only speak English. So much fun. But I sucked it up, accepted the challenge, and I improved it.

It really doesn't take a super-genius to code javascript simply, efficiently, and without errors. Funny that a lot of programmers that think they're very smart are the ones that either shit on javascript, or make a lot of stupid errors with it, or both.

I've seen similar abuses with global maps in other languages (essentially the same). This is an architecture fault rather than a language fault.

As you say, that is a problem with any language and project with a revolving door of developers. Perhaps those companies should learn their lesson and hire at least one or two good, permanent senior devs to keep things on track.

Like always, the human factor outweighs almost everything else.

I'd love to have a way to take a `Date` object and set it as the value of a `datetime-local` input. Sure feels like that should be straightforward to do, without requiring any weird conversions like it does.
Wow. `zonedDateTimeISO` looks like exactly what I was hoping for. I'm kind of surprised that there isn't anything analogous to `strftime`, though.
That’s not what “objective” means.
"When I use a word, it means just what I choose it to mean —neither more nor less."
It's not often that one comes across a quote from one of the greatest works of art:

https://www.goodreads.com/quotes/12608-when-i-use-a-word-hum...

That whole conversation is quite interesting and perhaps this part can also be thought of as alluding to "prescriptivism versus descriptivism".

I think it goes beyond prescriptivism/descriptivism.

Descriptivism refers generally to describing how a community uses language, so the most common usages end up being the primary definitions. Once documented, these tend to become prescriptive.

In that context, a single author who uses a word in an unusual way would likely not have any impact on the descriptive or prescriptive definitions of that word, unless of course their usage becomes common.

Humpty was arguing for the benefits of using words in unusual ways, which potentially violates both prescriptivism and descriptivism.

Impressionistic use of words is one example of this, where the words used might convey a certain feeling, perhaps via their connotations, even though their literal definitions may not be exactly appropriate. This kind of usage is generally found in literature where language is being used as an artistic tool rather than a prosaic tool of communication.

tl;dr: my HN comments is a art