
JavaScript: Does It Mutate? - plurby
https://doesitmutate.xyz
======
ris
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.

~~~
goldenkey
Realize in C and JS and most languages, the standard equality '=' operator,
does exactly this.

For example, console.log(2*(a = 10)) would print 20 while setting a to 10

~~~
reificator
> _Realize in C and JS and most languages, the standard equality '=' operator,
> does exactly this._

> _For example, console.log(2_ (a = 10)) would print 20 while setting a to 10*

`=` is the assignment operator, not the equality operator. The equality
operator is `==` in C and `===` in JS.

~~~
goldenkey
You are correct, my mistake.

------
baby
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.

~~~
michaelmior
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.

~~~
baby
Yeah, 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
As 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
Didn'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.

~~~
michaelmior
True. Unfortunately I think that means ! just becomes very ambiguous since
it's not precisely defined. ("Dangerous" means different things to different
people.)

------
joshribakoff
[https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Array)

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
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.

~~~
incadenza
Yeah, similar to how lodash makes it explicit in the docs. That would be
great.

------
k__
Nice 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.

------
konsumer
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](https://codepen.io/konsumer/pen/bKKYJO?editors=0010)

This can make things very hard to troubleshoot.

~~~
konsumer
And whether it mutates is kind of a surprise. Like reduce doesn't, even though
it works similar to forEach, map, and every.

~~~
twiss
No, 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
ah, yeah, forgot acc param in reduce. updated.

------
mabynogy
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](https://p.sicp.me/h4MJE.js) and the compiled code
[https://p.sicp.me/ctr6M.js](https://p.sicp.me/ctr6M.js).

------
beaconstudios
just use ramda ¯\\_(ツ)_/¯

~~~
marcosdumay
Or 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
There 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
There 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
It'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.

~~~
marcosdumay
Looks like I misunderstood your comment (but it is sometimes about
performance, there are people on that performance above everything world).

Yes, you are going into it wrong. What I can see on that comment is that:

\- You expected to write a "mostly pure" program in a pure language. It won't
let you do that, you will write a pure program. You also do not need to be
careful in doing it. The most obvious thing you get on a strict language is
carelessly - you don't have to care about things that the language does not
allow. You focus on (re)writing your program.

\- As a consequence of the first point, the more strict (on anything,
including mutability) a language is, the easier it is to change old code,
because you can assume a lot of stuff about it, and because you care less
about making mistakes.

\- Specifically about mutability, it does not take any control away. Mutable
values are exactly as expressive as immutable ones. You can have mutability
semantics on an immutable language, and most try to make it as near a first
class syntax as possible, but the one change is that you will have to make it
explicit, one way or another.

------
hjek
Looking 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)

~~~
kbp
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).

------
mannykannot
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.)

~~~
jakelazaroff
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.

------
erikpukinskis
TL;DR: sort and reverse are the tricky mutators in my opinion. Everything else
is more obvious.

------
stockkid
It'd be useful to filter by whether methods mutate.

------
jawns
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.

~~~
sctb
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.

------
draw_down
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`.

