The 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.
> The greatest sinners are methods that both mutate and return their result.
Go's `append` builtin is guilty of exactly that sin. And worse, it does not always mutate its parameter in-place, so things get hellish if you start aliasing/shallow-copying slices because depending on the size of the underlying array, appends could overwrite the other slice's data or make the two slices diverge entirely.
I 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.
As someone who writes JavaScript full time, this is my biggest gripe with the language. Folks sorting arrays inline without first copying them is one of the things that I pretty consistently call out in code reviews.
That'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.
That'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.
Side note: I just tried solving an actual problem in julia for the first time. That language does a lot of stuff right, at least for my definition of right.
Also commented below but the idea here was not actually mutation but safe and dangerous versions of methods... E.g, below insert is mentioned and it has no safer version so it has no ! version... Elixir adopted this convention from Ruby also, but there is never any mutation in Elixir of course as BEAM doesn't allow it.
This is something ruby inherited from scheme. Scheme library devs are however a lot more militant about it than ruby devs, so everything that mutates has a ! after it.
Pytorch does something similar, except with an underscore at the end of the name. As the arguments are tensors, it's a really handy convention and the API communicates whether a method or function modifies an argument.
I don't believe this was always the case, so it's probably caused a lot of deprecation warnings for some people.
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.
I 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.
It 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
I 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 and the compiled code https://p.sicp.me/ctr6M.js.
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.
Somewhat 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).
You'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.
Three 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.)
Re: "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.
It'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.
Thanks, 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.
Well, 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`.
[+] [-] ris|7 years ago|reply
[+] [-] masklinn|7 years ago|reply
Go's `append` builtin is guilty of exactly that sin. And worse, it does not always mutate its parameter in-place, so things get hellish if you start aliasing/shallow-copying slices because depending on the size of the underlying array, appends could overwrite the other slice's data or make the two slices diverge entirely.
[+] [-] drinchev|7 years ago|reply
[+] [-] bastawhiz|7 years ago|reply
[+] [-] coldtea|7 years ago|reply
Python let's you do it, and you get "None" as the result, which makes such mistakes slower to discover.
This would require some way to mark a function as returning or void.
[+] [-] goldenkey|7 years ago|reply
For example, console.log(2*(a = 10)) would print 20 while setting a to 10
[+] [-] paulddraper|7 years ago|reply
[+] [-] quotemstr|7 years ago|reply
[+] [-] jacobush|7 years ago|reply
[+] [-] baby|7 years ago|reply
[+] [-] baby|7 years ago|reply
[+] [-] michaelmior|7 years ago|reply
[+] [-] stilley2|7 years ago|reply
Side note: I just tried solving an actual problem in julia for the first time. That language does a lot of stuff right, at least for my definition of right.
[+] [-] bglusman|7 years ago|reply
[+] [-] bjoli|7 years ago|reply
[+] [-] dpwm|7 years ago|reply
I don't believe this was always the case, so it's probably caused a lot of deprecation warnings for some people.
[+] [-] phyzome|7 years ago|reply
[+] [-] joshribakoff|7 years ago|reply
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.
[+] [-] sharpercoder|7 years ago|reply
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|7 years ago|reply
[+] [-] k__|7 years ago|reply
I'd maybe even throw out all non-mutating functions in the first place.
If it's on that page it mutates, the end.
[+] [-] konsumer|7 years ago|reply
This can make things very hard to troubleshoot.
[+] [-] konsumer|7 years ago|reply
[+] [-] mabynogy|7 years ago|reply
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 and the compiled code https://p.sicp.me/ctr6M.js.
[+] [-] beaconstudios|7 years ago|reply
[+] [-] marcosdumay|7 years ago|reply
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.
[+] [-] 3131s|7 years ago|reply
Or Lodash-FP...
[+] [-] loftyal|7 years ago|reply
[+] [-] hjek|7 years ago|reply
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
[+] [-] kbp|7 years ago|reply
[+] [-] klibertp|7 years ago|reply
[+] [-] lispm|7 years ago|reply
[+] [-] mannykannot|7 years ago|reply
[+] [-] jakelazaroff|7 years ago|reply
[+] [-] erikpukinskis|7 years ago|reply
[+] [-] stockkid|7 years ago|reply
[+] [-] jawns|7 years ago|reply
[+] [-] sctb|7 years ago|reply
[+] [-] michaelhoffman|7 years ago|reply
[+] [-] int0x80|7 years ago|reply
[+] [-] Filligree|7 years ago|reply
A better approach would be to edit the title here, and any other place linking to it.
[+] [-] draw_down|7 years ago|reply
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|7 years ago|reply
[deleted]