News for hackers. Stuff that matters.
Re:SICP: JavaScript Edition available for pre-order by muh_gradle on Thursday January 20, 2022 @07:52PM (#30017719)
I have a copy of SICP right next to me, and it's one of my favorite computer science books. I'm not sure how I feel about this javascript edition though...
Re:SICP: JavaScript Edition available for pre-order by kazinator on Thursday January 20, 2022 @08:11PM (#30017874)
I was doing some Javascript coding today.

A

   false != x
test failed for a zero-valued x; had to make it !==. WTF? I never want false and 0 to be other than different objects.

Almost every twist and turn in this language is an imbecillic clusterfuck.

I had one instance of a if (foo.bar = 0) typo; no warning from the implementation. Why do we want stupid C mistakes in a higher level language, without the C fixes for them?

   list.forEach(function (item, i) {
     ...
   });
makes my eyes bleed. JavasSript borrows the syntax of languages geared toward a form of computing at which JavaScript is poorly suited for; when you're combining functions together, it looks like dog's breakfast.

To get a value out of a switch statement, you have to wrap it in a function that is immediately called?

   function () {
     switch (WTF) {
     }
   }();
the switch statement has the idiotic break with fallthrough found in C.

One redeeming feature: A || B || C ... works a lot like (or A B C ...).

Unfortunately, this is required for the simple task of incrementing a nonexistent dictionary key from zero:

    dict[key] = (dict[key] || 0) + 1;
this calls for two dictionary key lookups. You want this sort of thing in a single operation.

Oops! In the following, j isn't lexical:

    for (let i = 0, j = 0; ....
I copy and pasted something like this from a StackOveflow answer into a recursive function and spotted that the j in a recursed frame was messing with the j in outer frames, there just being one j.

What is "function scope" and why even have something so idiotic? Just let, or GTFO! C has function scope for goto labels, that's it; why introduce something like that.

Appending together Lists has functional semantics, like Lisp:

   a.concatenate(b)  // new thing is returned, a stays the same, so you must do a = a.concatenate(b).
But

   a.push(b)         // mutates a in place.
Here is a shitshow, curently with a fitting number of upvotes: 666.

https://stackoverflow.com/questions/881085/count-the-number-...

I accidentally wrote

   if (foo.bar.indexOf[key] < 0) { code }
code was silently skipped even though foo.bar is an empty list. No diagnostic, nothing. Oh, indexOf is a function, which is an object. Every damned object is a dictionary with properties you can access as strings with square bracket indexing, and that is always safe: it yields undefined if the property is not found.
Re:SICP: JavaScript Edition available for pre-order by westoncb on Friday January 21, 2022 @05:19AM (#30021579)
I feel your pain, but these mostly fall into the same category of frustrations you'd get when doing something like switching operating systems, e.g. going from Windows to Mac or vice versa. You run into all kinds of things that seem stupid and you're bombarded with things that should "just work" and seem not to.

But, they disappear and are mostly non-issues once you use the alternate system/language regularly for a while. Some I think you will come to see as benefits, sensible, or find that there are trivial workarounds.

One quick improvement. The more commonly used contemporary syntax in JS for something like your list.forEach example is: `list.forEach((item, i) => ...)` —writing out the full `function` keyword is indeed an eyesore :)

Re:SICP: JavaScript Edition available for pre-order by kazinator on Friday January 21, 2022 @01:19PM (#30027662)
> Oops! In the following, j isn't lexical:

I don't think I'm correct here. However, be that as it may, in my code, when I moved that second variable before the loop and initialized it there with a let, the behavior somehow changed.

Re:SICP: JavaScript Edition available for pre-order by throwthere on Thursday January 20, 2022 @08:41PM (#30018108)
Not to minimize these problems but typescript eases a lot of them.
Re:SICP: JavaScript Edition available for pre-order by celroid on Friday January 21, 2022 @02:50AM (#30020457)
Once you learn the quirks of JavaScript you get used to it. Also you should not use forEach in JS, take a quick look at this page. https://phoenix35.js.org/good-practices.html
Re:SICP: JavaScript Edition available for pre-order by kazinator on Friday January 21, 2022 @01:15PM (#30027586)
If forEach is not to be used, it should be deprecated and removed from the language in a timely manner.

The objection that it's not functional is invalid; procedural traversal with a callback function, object or closure, is a time-honored pattern in software.

Most of the other problems cited are not with forEach per se but the language in which forEach finds itself.

None of the alternatives presented are more attractive than

  for (let i = 0; i < obj.length; i++) {
    let elem = obj[i];

  }
If we could just run this through the C preprocessor, we could have

  foreach (i, elem, obj) {

  }
by way of something like:

  #define foreach(ivar, elvar, obj) \
    for (let ivar = 0, elvar; \
         ivar < obj.length && (elvar = obj[ivar], true); \
         ivar++) 
Since Javascript is based on C syntax, it should have the preprocessor that the birthplace of C saw it fit for that language not to be without.
Re:SICP: JavaScript Edition available for pre-order by hajile on Friday January 21, 2022 @01:42PM (#30028051)
> If forEach is not to be used

That source is simply incorrect. forEach is perfectly fine to use as long as you realize what is happening. It iterates an array where each thing is a function that returns a promise. Of course the results aren't what they expect.

ES5 array additions suffer from being a little too early (though later stuff wouldn't exist otherwise, so...). They are designed to deal with holey arrays (arrays with indexes missing). This is extremely uncommon today, but was decently common once upon a time. They were also created before the iterator protocol.

The real fix is to design iterator versions that can handle things like async generator functions.

> it should be deprecated and removed from the language in a timely manner.

NOTHING can be removed from the language once added. Doing that would break all the older websites that depend on it (technically, a few minor breaks happened after they tested millions of sites and couldn't find anything that was adversely affected). At best, they can block older features from newer features. For example, using class syntax or a bunch of other ES6 language structures automatically makes your code shift into "use strict" mode.

I hope they introduce a "use strict 2" variant that strips away more of the undesirable features than the current "use strict" does.

> Since Javascript is based on C syntax, it should have the preprocessor that the birthplace of C saw it fit for that language not to be without.

That pre-processor was a source of untold nightmares. Direct injection leads to bugs. If someone is going that route, full-blown macros are the only answer. There is a full-blown macro system Mozilla created a few years ago, but it's not very popular.

https://www.sweetjs.org/

There also exist some C-style pre-processors for babel too, but they should be avoided because lisp's gensym is a critical feature that they and C both lack.

Re:SICP: JavaScript Edition available for pre-order by hajile on Friday January 21, 2022 @01:29PM (#30027855)
Some of this is a lack of familiarity. You wouldn't dream of using C without reading a book first. You wouldn't dream of tackling a Java project without reading on the topic. You wouldn't even dream of trying something as simple as Python without a bunch of reading.

Why do you try to code JS without reading about the language? It's the only language where people consistently try this kind of thing.

> false != x

Referential equality operator is `!==` (which isn't so different from lisp where you have eq, eql, equal, string=, etc). Fun fact: Eich didn't have type coersion in the language at first. It was added at the insistence of developers (something he regrets, but Microsoft forced this to stay in the spec)

> function () { switch (WTF) { } }();

A lot of languages don't make switch statements into expressions. The fact that JS can do this via a function while most other popular languages cannot do it at all is a benefit if anything (there is a proposal to allow these to be expressions). More generally, prefer this

    let foo
    switch (abc) {
      case "blah":
        foo = 123
        break;
    }
> the switch statement has the idiotic break with fallthrough found in C.

It actually gets this from Java. Eich was told to make the language look like Java and he did.

> dict[key] = (dict[key] || 0) + 1;

Your code is potentially bad. `||` uses "falsey" values. If the key is zero, the left-hand side will return false forcing the right-hand side. Use `dict[key] ?? 0` instead as it will only return the right-hand side if the left side is `undefined` or `null`.

If it is a pure dictionary, you are using the wrong construct too. Use `Map` instead

    let dict = new Map()
    dict.set((dict.get(key) ?? 0) + 1)
> this calls for two dictionary key lookups. You want this sort of thing in a single operation.

You get a value rather than a reference. I believe the JIT can optimize this pattern. If you are really fixated on using a reference, store an object with a value instead so you can grab and keep an object reference.

> What is "function scope" and why even have something so idiotic?

In lisp, you'd use `(let ((foo 123)) (print foo))` to set an explicit scope. ES4 proposal had a let block which I think would have been a better idea than the current stuff. Function hoisting generally occurs in other languages one way or another, but is hidden from the user.

> for (let i = 0, j = 0; ....

Yep, this is one more reason why the correct fix was a let block with `let (i=0, j=0) for (; ...)` or 1et for (var i=0, j=0...`

> Appending together Lists has functional semantics, like Lisp:

Array.prototype.concat goes back to at least the ES3 spec as does Array.prototype.push. At the time, arrays were either implemented as hashtables or linked lists. My guess is that adding a single entry to the hashtable only occasionally caused a resize while concatenating N lists was almost guaranteed to do so. I think they decided that it was easier for implementors to just create a new reference.

Since ES5, most new array APIs have favored returning new lists for everything. I think there's a case for making new versions of those older APIs that behave immutably.

> if (foo.bar.indexOf[key] < 0) { code }

This could happen in any language with a methodNotFound handler. For what it's worth, `if (foo.bar.contains(key)) {code}` would be the better way of writing it. I think Typescript would also catch this (though I'm not 100% as I don't remember ever having made that particular mistake).

> Every damned object is a dictionary with properties you can access as strings with square bracket indexing, and that is always safe: it yields undefined if the property is not found.

Garbage In; Garbage Out. At least it handles that garbage in a safe manner. What more could you ask from a dynamic language?

Re:SICP: JavaScript Edition available for pre-order by kazinator on Friday January 21, 2022 @03:21PM (#30029445)
> Why do you try to code JS without reading about the language?

Because it's painful drivel.

> Your code is potentially bad. `||` uses "falsey" values

We know that this dictionary will only contain things to which we will be adding 1, which we expect to be numerically done.

(I have already made the point that there shouldn't be multiple falsey values.)

I mean, there could be other problems in this direction: if a string is in there like "foo", then the + operation will make it "foo1".

It's unlikely we would ever want to code a "add 1 to dictionary element, treating non-existence as zero" which will work for absolutely any dictionary regardless of knowing/assuming anything about its content.

> This could happen in any language with a methodNotFound handler.

Only if you define that handler, and have it silently return.

Handlers for method not found are a very poor idea to include in an object system. Ruby and its ilk uses it for ugly, monkey patched solutions for this and that.