4 ms·
Stuff like this is what drives me crazy when I work with dynamic languages like JS or PHP. I have seen a lot of code that looked perfectly fine but suffered fro
by maxxxxx 7y ago
Stuff like this is what drives me crazy when I work with dynamic languages like JS or PHP. I have seen a lot of code that looked perfectly fine but suffered from unexpected casts or defaults.
I much prefer languages like C#, C++ or TypeScript where the compiler warns me of such problems.
- bastawhiz 7y agoThis has nothing to do with casts or defaults. Map passes multiple parameters and parseInt accepts multiple parameters. Being unaware of the functions you're using is the problem, not some sort of weird gotchas of the language. The code in the title makes multiple assumptions, and those assumptions all proved wrong.
- teraflop 7y agoI mean, I get that this particular example isn't really all that mysterious, but I think it's fair to say that "all parameters are optional and extra parameters are ignored" is a Javascript language gotcha.
- gameswithgo 7y agoA typed language would absolutely fail this at compile time.
- deleted 7y ago[deleted]
- zbentley 7y agoOnly 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.
- tomxor 7y agoYes 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.
- horsawlarway 7y agoI'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)
- ernst_klim 7y ago> I'd argue basically everything about mandatory arguments is worse than having edgecases like this. Any sane language having optional arguments is using keys for opt args. That's what common lisp and OCaml do. In OCaml you would write: let parse_int ?(radix = `Dec) string =... val parse_int : ?radix:[`Bin | `Oct | `Dec | `Hex] -> string -> int and call it like parse_int "42" - 42 parse_int ~radix:`Bin "111" - 7 List.mapi parse_int ["1"] - Static error: this expression has type `string -> int` but expected int -> 'a -> 'b
- happytoexplain 7y agoPrecisely - this is an API design failure, pure and simple. Not a problem with typing, optional arguments, etc.
- wutbrodo 7y agoIt'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.
- zbentley 7y agoWhat does this have to do with best-effort parsing? "Variadic arguments" (really optional arguments with defaults/signature overloading, which is only arguably the same thing) existed long before JavaScript, and, indeed, the web. Optional arguments are also widely considered to be good/useful.
- bastawhiz 7y agoTyped languages can still have overloaded functions and optional parameters. You could easily encounter a similar issue in many typed languages.
- ajanuary 7y agoI would argue Javascript silently accepting more arguments than the function signature declares is kinda a gotcha. And that's what leads people to not realise map is passing extra arguments, because `list.map(a => f(a))` works.
- tomxor 7y agoI think the parent is talking about how C for instance does not have optional parameters (for better or worse), if this was the case for JS everyone who has ever used parseInt would be aware it takes a radix, but then again you also wouldn't be able to just plug it into map arbitrarily. Not saying one is better than the other, optional parameters can make for much less verbose code, I especially like parameter defaults introduced in ES6.
- notus 7y agoWhy would it drive you crazy? This is an example of someone not even knowing how the map function works.
- nilkn 7y agoI would certainly consider this a "gotcha" because it's unexpected that map both deviates from the norm and the language makes it so easy to misuse and hide that misuse. This reminds me of "array set" in Tcl (a language many probably haven't used in a long time -- or ever). Unlike the normal "set" command in Tcl, which overwrites the full value of the variable, "array set" is actually a merge operation -- it merges in new keys and doesn't get rid of anything. I've seen experienced programmers use "array set" in a loop, leading to awful bugs. This too could be dismissed by saying that they "don't even know how array set works," but actually I place the blame on the language for making it so easy to misuse.
- maxxxxx 7y agoOnce you work with several languages it’s really hard to keep track of all this stuff. I never know what exactly evaluates to true vs false in PHP or JavaScript for example. Add to that sloppy programmers on your team who don't check their assumptions it’s really easy to have a ton of subtle bugs in your code.
- fieryscribe 7y ago> I never know what exactly evaluates to true vs false in PHP or JavaScript for example. You don't have to know if you don't use implicit type-casting. Make it explicit and you won't really have to worry about it. "Explicit is better than implicit" is part of the Zen of Python for a very good reason.
- maxxxxx 7y ago"You don't have to know if you don't use implicit type-casting. Make it explicit and you won't really have to worry about it. " Correct. Unfortunately a lot of people use implicit casting a lot and have no idea that that's what they are doing.
- epidemian 7y agoTypeScript doesn't complain about this[1], as the type of parseInt matches the type of Array.prototype.map parameter. [1]: https://www.typescriptlang.org/play/#src=console.log(%5B'1'%2C%20'7'%2C%20'11'%5D.map(parseInt))%0A https://www.typescriptlang.org/play/#src=console.log(%5B'1'%...
- acdha 7y ago> I much prefer languages like C#, C++ or TypeScript where the compiler warns me of such problems. parseInt is defined as accepting a string and an optional radix value, which is numeric. map is defined as providing the value and its index, which is also numeric. Would any of C#, C++, or TypeScript catch that without redefining either parseInt or map to require a more specific type, breaking compatibility with many millions of lines of code around the web?
- dehrmann 7y ago> Would any of C#, C++, or TypeScript catch that C# would have a compile error with that map and parseInt definition because it can't coerce the types.
- acdha 7y agoparseInt and the map callback signature both declare the second argument as integers: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt#Parameters https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Parameters https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... The thing which would actually catch this would be the mismatch in the number of arguments (modulo someone declaring that third argument as optional) or breaking compatibility to change one of them not to be a basic integer.