22 ms·
JavaScript: Does It Mutate?
- sharpercoder 8y agoI have done quite some implementations now of things in an immutable way. Easy to test, easy to spot bugs, easy to work with. Come back a few months later, and purity is removed on many things despite clear comments on the why it is immutable. I welcome any effort to make developers aware of immutability. One simple thing to help here is to have the "mutability: flag marked on every method in MDN.
- incadenza 8y agoYeah, similar to how lodash makes it explicit in the docs. That would be great.
- jawns 8y agoIt's clear at first glance to someone familiar with the language that the site is about Javascript methods, but for the benefit of those who are not very familiar, it would be nice if the site called that out in the title or a subhed.
- int0x80 8y agoThat was exactly my first thought. Do we just assume javascript this days? Not even a title or two liner of what is going on.
- michaelhoffman 8y agoI work in computational genomics so programming languages in general was not even the first thing I thought of.
- Filligree 8y agoWhy should the site need to call that out? It's about Javascript. A better approach would be to edit the title here, and any other place linking to it.
- sctb 8y agoThanks, we've updated the title. We don't always need to call out such things, but when a headline has broader enticement than what it actually delivers then we're into misleading or clickbait territory.
- k__ 8y agoNice idea, but for a website that tells you about what functions mutate, its indicator for this property is rather small. I'd maybe even throw out all non-mutating functions in the first place. If it's on that page it mutates, the end.
- ris 8y agoThe greatest sinners are methods that both mutate and return their result. It's possible for people to use some of these for years and not realize they're mutating. This is something python has largely got right.
- baby 8y agoThat's OpenSSL for you.
- jacobush 8y agoWhich is interesting given how easy it is to mutate in Python.
- hellofunk 8y agoEasy, yes. And more importantly, explicit, unlike the JS examples in this article.
- nerdponx 8y agoThis is only because good precedents were set by the built-in functions. Mutating functions return None, and only non-mutating functions return something non-trivial. It's purely convention, but therein lies the power of the core language setting a good example.
- drinchev 8y agoI did that mistake with `sort`. There was a nasty bug that I tried to fix, when I realised that `sort` actually mutates the array and returns it as well. That was pretty good lesson to learn.
- aidos 8y agolist.sort returns None in python - and I’m sure it’s been that way for a long time. sorted is the function you mostly want as it returns a new list each time.
- 8y ago
- baby 8y agoThat's why I really like the ! In Ruby. If used in a function name it indicates that it will mutate its arguments. It's so straightforward instead of just not knowing or having to use references or pointers.
- michaelmior 8y agoThat's not really true. First, it's entirely a convention and isn't always followed. Second, there are many many methods which mutate objects and don't have a bang. For example, most of the methods which mutate arrays.
- baby 8y agoYeah, the second part of my comment was wishful thinking. It would be cool if it worked similarly to how Golang capitalizes function names to make them public.
- trampi 8y agoAs someone who has worked not very long with Ruby I have also stumbled upon the "!"-methods. It is a great convention - but as already noted, not consequently used. Have a look at the insert method on array: insert(index, obj...) -> ary From the signature and the convention one could think, that it returns a new array with the object(s) added at the specified index. However, insert mutates the array and returns a reference to the just mutated array, probably for chaining. However, a convention that is not completely followed in the standard library like this does more harm than good as it can confuse newcomers. And you can not change it due to backwards compatibility.
- bglusman 8y agoDidn't know whether to respond here or above to parent, but ! In stdlib of Ruby does not indicate mutation but "dangerous version"... Often the same thing but I believe all methods in std lib with a bang also have a noo-bang version, which usually does not mutate receiver, but could also be, for example, File.read vs File.read!... I forget if Ruby has those but Elixir does and they indicate there that one returns an error or ok prefixed tuple but the dangerous version just raises an error if the file isn't present for example.
- beaconstudios 8y agojust use ramda ¯\_(ツ)_/¯
- 3131s 8y agoThat's my feeling too, would rather not have to trust myself to remember any of this. Or Lodash-FP...
- marcosdumay 8y agoOr Haskell, or a lot of other languages. It's so empowering to be able to answer this question with an instantaneous "no, of course not, it can't be". People that never experienced it have no idea.
- jstimpfle 8y agoThere is another version of this question, "now how do I change this thing? I designed my program so carefully to be almost completely pure, but now I want to try this other thing and I'll have to rewrite the code. To avoid that I will use this other hack that is unreadable and has sub-optimal performance" (Maybe I'm just doing it wrong?)
- marcosdumay 8y agoThere are two different large worlds in computing. In one of them it matters if your computer will stop for 10ms ever couple of minutes, or if you use an extra 20MB of memory. This is the world of C, Rust and assembly, and you'll be stupid to try high level code structures here. The other world is where mostly everybody lives. Here having your code feature complete and correct is overwhelmingly more relevant than putting it above the 95th percentile in performance, and you would be stupid to keep adding complexity just because it's sub-optimal. It should be very clear what world you are in, because there are actually very few borderline problems.
- jstimpfle 8y agoIt's not about performance, at least not 95th percentile performance, by far. It's almost never about that, and it's not what I was talking about. I'm totally fine with less than 20% almost always, and often performance just doesn't matter at all (as you say). But - if an architecture is wrong, sometimes the best you can get without rewriting everything could be like 1%. Maybe the requirements can simply not be fulfilled this way. In any case, my comment was mostly about the development process. I don't want to start a heated discussion (I've started too many of them in the past) so all I will say is what I care about is control, in multiple ways.
- mannykannot 8y agoThree other questions of similar importance are "which exceptions might it throw?" "what are its exception-safety guarantees?" and "is it thread-safe?" (they do not all apply in all cases, of course.)
- jakelazaroff 8y agoRe: "is it thread-safe?", the answer is yes. JavaScript is single-threaded and, since SharedArrayBuffer was disabled in all major browsers in response to Spectre, there's no concept of shared memory.
- draw_down 8y agoWell, you'd hope the read-only functions like `every`, `map` etc don't mutate. For the ones that do, it might be a good idea to provide examples of how to accomplish the same thing but without mutating. For example, show how to use `concat` where you'd use `push`.
- 11235813213455 8y agoyou should avoid Array.prototype.(un)shift (https://repl.it/@caub/stupid-unshift https://repl.it/@caub/stupid-unshift), Array.prototype.splice, in and delete operator
- hjek 8y agoLooking at a site like this makes me appreciate a language like Clojure more. I was quite surprised a while ago when I learned that `sort` in Common Lisp can mutate somehow randomly. > The sorting operation can be destructive in all cases. http://clhs.lisp.se/Body/f_sort_.htm http://clhs.lisp.se/Body/f_sort_.htm
- klibertp 8y agoYou'll find people much better suited to explain this on HN, but I'd guess it's about efficiency. CL was defined long ago and one of the goals was for it to be fast. It really is, but there is a pervasive mutability in most of it because of this, with functions like nreverse, rplca, nconc and so on.
- lispm 8y agoAdd something like this to your library and you are fine: (defun isort (sequence predicate &key (key #'identity)) (sort (copy-seq sequence) predicate :key key))
- kbp 8y agoSomewhat relatedly, a very common mistake in Lisp is modifying literals; a lot of people don't realise that there's a difference between writing '(a b c) and writing (list 'a 'b 'c), but QUOTE's description in the spec plainly says "The consequences are undefined if literal objects (including quoted objects) are destructively modified." To demonstrate the difference in practice (DELETE is the destructive version of REMOVE): CL-USER> (defun abc-without (letter) (delete letter '(a b c))) ABC-WITHOUT CL-USER> (abc-without 'a) (B C) CL-USER> (abc-without 'b) (A C) CL-USER> (abc-without 'c) (A) This behaves strangely because it modifies the constant list (A B C) rather than consing up a new one on each invocation (like calling LIST or COPY-SEQ would), so by the end the list it passes to DELETE has been reduced to (A).
- konsumer 8y agoIt should be noted that you can still mutate in these. Like it's not at all recommended, but you have access to the original array in many of the functional "non-mutating" Array methods, for example: https://codepen.io/konsumer/pen/bKKYJO?editors=0010 https://codepen.io/konsumer/pen/bKKYJO?editors=0010 This can make things very hard to troubleshoot.
- konsumer 8y agoAnd whether it mutates is kind of a surprise. Like reduce doesn't, even though it works similar to forEach, map, and every.
- twiss 8y agoNo, all of them do: https://codepen.io/anon/pen/bKKaBy?editors=0010 https://codepen.io/anon/pen/bKKaBy?editors=0010 All of these pass the original array as the last argument, reduce()'s callback just takes one more argument, hence the confusion in the original codepen. It would indeed be highly surprising if some of these functions passed a copy of the original array.
- konsumer 8y agoah, yeah, forgot acc param in reduce. updated.
- joshribakoff 8y agohttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... Under "instances" there is a list of mutators, accessors, and iterators that I find more useful since it is grouped and alphabetized. Another pitfall I have seen Dev's make is to assume .filter() will return the original array even if the callback returns true for every element. In fact it always returns a copy, even if all elements pass the filter I recommend the npm package deep-freeze in conjunction with unit testing, makes this way easier to debug.
- mabynogy 8y agoI use those functions a lot and I'm still impressed by how fast they are ran by current JS interpreters (especially V8). I did a layer over strings/arrays/objects to get nearly the same behavior everywhere. An example of those functions (in my custom programming language) https://p.sicp.me/h4MJE.js https://p.sicp.me/h4MJE.js and the compiled code https://p.sicp.me/ctr6M.js https://p.sicp.me/ctr6M.js.
- stockkid 8y agoIt'd be useful to filter by whether methods mutate.
- erikpukinskis 8y agoTL;DR: sort and reverse are the tricky mutators in my opinion. Everything else is more obvious.