Using Gremlins (1984) to Understand Non-Mutating vs Mutating Methods in Ruby

Hayley Keefer
Oct 30, 2020 · 7 min read

With Halloween just a couple of days away, what better time to use a horror (okay, comedy horror) film to help us understand a programming concept? While studying for my upcoming Launch School RB109 assessment, the idea popped into my head to use the movie Gremlins as an analogy for non-mutating and mutating methods.

If you haven’t seen Gremlins, here is the information you need to know before continuing:

Mogwai are cute little creatures; in the movie, the main character Billy (a human) is given one as a gift. His name is Gizmo and he is sweet and adorable!

Image for post
Image for post

There are three rules Billy must follow to care for Gizmo:
1. Do not expose him to light (it will kill him!)
2. Do not let him come into contact with water.
3. Do not feed him after midnight.

Well, Gizmo has water spilled on him accidentally, which causes multiple mogwai to spawn from his back. They are cute, like Gizmo, but devious.

The newly spawned Mogwai

The ring leader of these new mogwai tricks Billy into feeding them after midnight. Soon after enjoying their cold chicken, the mogwai form cocoons, out of which they hatch and enter the world again as gremlins- horrible, ugly creatures who wreak havoc on the town!

Image for post
Image for post

Okay, that is all you need to know to follow my analogy. Let’s continue.

Non-Mutating Methods

Non-mutating methods in Ruby do not perform any modifications on the object the method is called on. Instead, invoking a non-mutating method on an object creates a new object, which is a copy of the original, and then modifies that object. The original object is unmodified and remains the same as it was before the method call.

In my mental model of non-mutating methods, I see the method invocation as a two step process:
1. A new object is created, which is a copy of the original caller.
2. Some modification of the new object is performed by the method, resulting in an object that looks different than the original.

This two-step process is just like how a gremlin is created!
1. A mogwai comes into contact with water and spawns a new mogwai, a copy of the original.
2. The new mogwai eats after midnight and mutates into a reptilian creature, much different from the cute mogwai it spawned from!

Here is a visual representation of the mogwai — gremlin process:

First, there is the original mogwai, Gizmo.

Image for post
Image for post

The first step to create a new gremlin while the original mogwai remains the same is to have the original mogwai touch water. This creates a new mogwai, a copy of the original.

Image for post
Image for post
After coming into contact with water

The second step of the process is for the new creature, the copy, to eat after midnight. Cold chicken, anyone?

Image for post
Image for post
After eating past midnight

Only the new creature ate after midnight, so it was the only one to mutate. Look at Gizmo! Still as cute as ever.

What would this look like in code?

gizmo = 'mogwai'
new_creature = gizmo + ' gremlin' # non-mutating method
gizmo # => 'mogwai'
new_creature # => 'mogwai gremlin'

First, local variable gizmo is initialized to a String object with the value 'mogwai’.

Then, local variable new_creature is initialized to the return value of calling String#+ on the object referenced by gizmo with an argument of ' gremlin’.

Here is what this method call looks like in the two step mental model:

Start with the original object:

Image for post
Image for post

First step of the method invocation is to create a copy of the original:

Image for post
Image for post

Second step is to modify the copy:

Image for post
Image for post

The second step of the process acts only on the new object, so there is no modification to the original!

Mutating Methods

Now let’s see what using mutating methods looks like. If we continue with our Gremlins analogy, calling a mutating method would be equivalent to Gizmo, the original mogwai, eating after midnight and becoming a gremlin himself. (Of course, he would never! He is too smart. But hypothetically speaking.)

Our mental model for understanding mutating methods should be that when calling a mutating method, there is only one step in the process. Unlike non-mutating methods which create a copy of the caller before performing modification on the copy, mutating methods perform modification directly on the caller.

This example will look similar to the one used to describe non-mutating methods. Remember, we ended up with two String objects after calling String#+ on the object referenced by gizmo. How many objects will we have after calling String#<< on the object referenced by gizmo?

gizmo = 'mogwai'
gizmo << ' gremlin'
gizmo # => 'mogwai gremlin'

String#<< is a mutating method and appends the string passed to it as an argument, ' gremlin’, to the calling object, the object referenced by gizmo. This permanently modifies the object referenced by gizmo, which now has a value of 'mogwai gremlin’. We started with one object and ended with one object.

Before method invocation:

Image for post
Image for post

After method invocation:

Image for post
Image for post

Arrays and Hashes

When called on an array, a non-mutating method will create a new array and then on that new array perform some sort of operation depending on the method, leaving the original array unmodified. Again, it is a two step process.

A mutating method called on an array will perform its modification directly to the array, permanently changing it. There is only one step; no new object is created.

The same mental model applies to calling methods on hashes. Two steps for non-mutating, one step for mutating.

Let’s call the non-mutating method Array#map on an array of strings.

array = ['gizmo', 'stripe']new_array = array.map { |name| name + ' mogwai' }p array                       # => ['gizmo', 'stripe']
p new_array # => ['gizmo mogwai', 'stripe mogwai']

First array is initialized to an Array object with a value of [’gizmo’, 'stripe’].

Then new_array is initialized to the return value of calling Array#map on the object referenced by array with a block passed to the method as an argument.

First, Array#map creates a new empty array, where it will place some object (determined by the return value of the block) on each iteration. It will return this new array once the method has finished iterating through the original array.

Next, Array#map iterates through each element in array, passing each one to the block where the block parameter name is assigned to the element. In the block, String#+ is called on the element referenced by name with an argument of ' mogwai’. This returns a new string object. Since this is the last line evaluated by the block, this return value is also the return value of the block. Array#map places this return value into the new array.

We now have two Array objects, with array remaining unmodified after the method invocation. Modifications were only performed on the new array.

Let’s do the same thing but with Array#map!, a mutating method. Remember, this method invocation will skip the copy step and will act directly on the calling object.

array = ['gizmo', 'stripe']array.map! { |name| name + ' mogwai' }p array                       # => ['gizmo mogwai', 'stripe mogwai']

I won’t describe everything that’s happening here because it would just be copying and pasting most of the description for Array#map. The only difference is that no new array was created; the return value of the block replaces the current element that was passed to the block. array has been permanently modified.

Conclusion

I hope my Gremlins analogy will help you remember what happens when we call non-mutating and mutating methods. Non-mutating method calls are two step processes: spill the water first to create a copy, then have the copy eat after midnight so that it is the only thing that mutates. Mutating method calls are only one step: the original eats after midnight and mutates himself.

One last look at the super cute Gizmo, doing a little dance. Adorable.

The Startup

Medium's largest active publication, followed by +775K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store