Live data from Hacker News

Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

medium.com

121–130 of 163 posts

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#121
post #97

Earlier quoted context omitted.

> - The language design allows your brain to ignore the index parameter because JS accepts superfluous parameters, which is a terrible decision. Without that we would not be able to have variable length argument lists in the past.

Yes, but again that's the consequence of another bad design: not including spread or another metaprogramming system for this. And not having default values either. Spread has existed in Python forever under the name of "splat operator", default values as well. Same with ruby. That what I meant when I said "they accumulate". A bad design decision not only affect the user cognitive load and productivity, but it also ca…

Whenever I learn about another of these Javascript foot-guns, I come away with a greater sense of awe at the relentless good sense that went into the design of Python. Not sure if it's a kind of super-human Dutch ability to foresee ramifications of design decisions, or if it's just that Python developed organically over a period of time with healthy feedback from smart and invested users.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#122

Earlier quoted context omitted.

I don't understand why this is downvoted to death without any replies. Sounds like a reasonable opinion but I'd also love to hear why people think it's downvote-worthy.

Not a downvoter, but I'll have a go: You might be a dev who isn't very familiar with Javascript, but you still need to fix it. Or you might be having an off day. Or you're writing a critical fix under a lot of pressure and simply forget about the issue. And I'll bet if this code was being reviewed, most devs would still overlook the issue. There are many real world scenarios where it isn't so easy or clear cut. We're…

The hilarious thing about the get-good arguments is that it's proponents don't realize that they're talking about expending effort _for nothing in return_, especially in cases like the server-side where nobody is forced to use Javascript.

You can say the same thing to Pythonistas who are fussed by memory management, but the tradeoff of that intuitiveness is performance (which is being somewhat obviated by newer languages like Rust, which have their own challenges to learn). The actual task you're trying to do is more complex, so the knowledge required is too.

In Javascript's case, the thing you're becoming an expert in is the path-dependent history of dumb language decisions by people who never should have been let anywhere near a language. It's beyond me that people don't understand why this would bug some, at the very least because it was so easily avoidable.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#123
post #97

Earlier quoted context omitted.

> - The language design allows your brain to ignore the index parameter because JS accepts superfluous parameters, which is a terrible decision. Without that we would not be able to have variable length argument lists in the past.

Yes, but again that's the consequence of another bad design: not including spread or another metaprogramming system for this. And not having default values either. Spread has existed in Python forever under the name of "splat operator", default values as well. Same with ruby. That what I meant when I said "they accumulate". A bad design decision not only affect the user cognitive load and productivity, but it also ca…

> in Python forever under the name of "splat operator"

The name “splat” is not commonly used in Python. That name comes from Ruby (or Perl?). In Python it is usually called “star” or similar.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#124

So in summary: JS map doesn't behave like anybody else's map. JS argument passing prefers doing the wrong thing to interrupting the programmer. These two things conspire together and poor parseInt does its best with the resulting garble.

I don't really see any issue here.

JS map behaves sanely - It adds extra params, but I've had them be useful every now and then, and I've never had them cause a problem.

Arguments behave... Like arguments behave in JS. Arguments have ALWAYS been variadic and accessible through the "arguments" variable within a function. That's not the "wrong" thing, it's just a thing. If you want named arguments - put them up top. Otherwise you'll get an array-like with everything passed. This is entirely consistent with the language.

Finally - These two things conspire together to do what exactly? This isn't a subtle bug where it works correctly 99% of the time and blows in prod late on a friday. This is an obvious error with even dead simple test cases that clearly show the dev has messed up.

Unit test your shit, or hell, just run it once or twice before using it and you're fine.

---

Basically - you should know how your std library works. That includes JS. I think this is pretty trivial. Worse, I actually find this behavior far more intuitive than something like ConfigureAwait(false) in C#, for example.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#125
post #121

Earlier quoted context omitted.

Yes, but again that's the consequence of another bad design: not including spread or another metaprogramming system for this. And not having default values either. Spread has existed in Python forever under the name of "splat operator", default values as well. Same with ruby. That what I meant when I said "they accumulate". A bad design decision not only affect the user cognitive load and productivity, but it also ca…

Whenever I learn about another of these Javascript foot-guns, I come away with a greater sense of awe at the relentless good sense that went into the design of Python. Not sure if it's a kind of super-human Dutch ability to foresee ramifications of design decisions, or if it's just that Python developed organically over a period of time with healthy feedback from smart and invested users.

It's also about the ability to say "fuck off".

People came at Guido relentlessly for adding new features, debating his decisions, trying to change the philosophy and aesthetic of the language.

The guy stood up to them for 20 years, staying polite but resolute at doing things his, at the time controversial, way.

To me, that's even more impressive than being a good language designer.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#126
post #50

Earlier quoted context omitted.

Only because map's third provided argument exists. If map only supplied element and index to the callback, this would pass most type checkers I'm familiar with, and would remain just as confusing.

Yes but with mandatory parameters the programmer would be aware parseInt had a radix in the first place if they had used it even once. That's one advantage of mandatory parameters, but there are of course disadvantages.

I'd argue basically everything about mandatory arguments is worse than having edgecases like this.

Honestly, most languages STILL won't catch this, because the typical pattern is to use method overloading to provide multiple signatures if default arguments aren't supported.

parseInt(in) parseInt(in, radix)

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#127

Earlier quoted context omitted.

Yes, but again that's the consequence of another bad design: not including spread or another metaprogramming system for this. And not having default values either. Spread has existed in Python forever under the name of "splat operator", default values as well. Same with ruby. That what I meant when I said "they accumulate". A bad design decision not only affect the user cognitive load and productivity, but it also ca…

> in Python forever under the name of "splat operator" The name “splat” is not commonly used in Python. That name comes from Ruby (or Perl?). In Python it is usually called “star” or similar.

Both are used.

Here you can see Steven D'Aprano, one of the dev of the Python stdlib, naming it "splat": https://mail.python.org/archives/list/python-ideas@python.or...

I use "splat" all the time myself.

On the other hand, itertools.starmap() is named this way because it does:

    def starmap(func, iterable):
        for e in iterable:
            yield func(*iterable)
Things rarely have one name in computing. Hell I call curly brackets "mustaches" all the time. It's as hard as cache invalidation apparently.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#128
post #65
post #53

Earlier quoted context omitted.

But see also https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... and especially https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... — that comment is increasingly stale unless you support browsers which are no longer supported by their vendors like IE8.

Good point. I've edited my comment to be more qualified.

The web is a fascinating study of advice that changed over time: there’s something like a half-decade window where the two argument form became common, with unsafe usage before and safe usage after.

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#129

Earlier quoted context omitted.

All languages I know of _default_ to a radix of 10 though, allowing other radixes where a prefix is added like 0b111010, 0xa7b45fd9, etc. Seems intuitive that if parseInt allows omitting the radix parameter, that it should default to whatever radix a standard integer primitive would default to.

> Seems intuitive that if parseInt allows omitting the radix parameter, that it should default to whatever radix a standard integer primitive would default to. But in this case, the radix is _not_ omitted. The map function passes the index of the current iteration as the second parameter to the function it is passed. It is kinda like this: ``` ['1','7','11'].map((item, index) => parseInt(item, index))

Thanks for connecting the dots for me, I see it now!

Re: Why ['1', '7', '11'].map(parseInt) returns [1, NaN, 3] in JavaScript

#130

Earlier quoted context omitted.

Only because map's third provided argument exists. If map only supplied element and index to the callback, this would pass most type checkers I'm familiar with, and would remain just as confusing.

Precisely - this is an API design failure, pure and simple. Not a problem with typing, optional arguments, etc.

It's both. Silently accepting variadic arguments increases the odds of something like this happening. This case wouldn't have happened without it.

Though as I said in another comment, best-effort parsing was a decision that arose out of the Web ecosystem and its one of the few things about Javascript's language design that can't br blamed on incompetence.

Post reply on HN