Speaking in Ruby

Rebecca Biancofiore
12 min readJan 23, 2023

--

Grammar tips for writing about Ruby — for students preparing for Launch School’s first written assessment or anyone who wants to get better at describing Ruby code in written or spoken English.

Photo by Ivan Shilov on Unsplash

Reading lines of Ruby code aloud might seem like a pointless exercise, but it’s incredibly helpful for A) showing you where there are gaps in your knowledge and B) forcing you to get intimately familiar with what every character is doing. Also C), if you’re a Launch School student preparing for your first written assessment, it’s not optional.

I said “reading” Ruby, not “describing the code.” You could also say “translating” Ruby. The point is, the code is already speaking, and explaining it aloud or in writing is a matter of getting as close as possible to conveying what it says in complete English sentences.

Ruby is a language. Yes, it’s a computer language, and as such is meant to be read by a computer, not by humans. Although it does have a reputation for being particularly readable for humans.

Language is language, programming or otherwise. Ruby has its own vocabulary (“string” is a Ruby word; “sentence” is not). More importantly, it has its own grammar, or rules for how the language can be used (you can say 5.times but not times.5).

Grammar is of the utmost importance when it comes to fluently and effectively using a language so that it makes sense. In English, we can’t say “Ball blue under car rolled chased dog.” The rules don’t let us. But if you try to decipher the gibberish, you can understand the underlying point of the sentence.

It doesn’t matter whether you’re working with a programming language or a spoken one. How much you can achieve from within the confines of the rules of your chosen language depends on understanding how those rules work and how they can be manipulated.

Grammar Is a Piano I Play By Ear

A Small and Hopefully Painless Grammar Lesson

In English, much of the time we use what’s called the “active voice.” It follows the word pattern subject-verb-object (SVO).

“The dog ate the bone.” “Dog” is the subject, “eats” is the verb, “bone” is the object. The subject is the doer, and the object receives the action. The subject is the star of the sentence. Subject has agency, while object is tacked on at the end so something can be done to it. Like getting eaten.

“I hate brussel sprouts.”

“My mom loves birdwatching.”

These also follow the SVO sentence order. However, it’s possible to switch around the cast of our sentence, giving the object a chance to shine: “The bone was eaten by the dog.” Who’s the star now? All we had to do was tinker with the verb form slightly, and voila. The passive voice lets the subject and object trade places.

Now, there’s nothing wrong with saying that the bone was eaten by the dog. What about the other examples:

“Brussel sprouts are hated by me.”

“Birdwatching is loved by my mom.”

Rough. BUT not wrong. These technically are grammatically correct. They’re just awkward and not how native English speakers typically formulate sentences. The passive voice has its limitations — but also its advantages.

We understand the implications of our native language’s grammar at a very early age. When my son is proud of something, he’s quick to let me know: “I cleaned my room!” The same goes for blame: “YOU forgot my backpack!”

And it’s amazing how early on children pick up on the power of the passive voice to disavow themselves of certain actions: “The water spilled.”

Notice how the water has spilled all by itself.

“The glass broke.”

These sentences are conspicuously lacking any reference to the agent of spilling and breaking — these things just happened.

Point being — five-year-olds have the natural ability to seamlessly switch to the passive tense when it suits their purposes — which means that an adult can easily do it too, although it might take a little intentional practice before it becomes natural. However, understanding that this is what you’re trying to achieve is half the battle.

And yes — this is exactly what you’re trying to achieve.

The Grammar of Spoken Ruby

In English, we need some type of subject holding our sentences together. Often we use clever workarounds for this, like the word “it”. “It’s rainy out today.” What is “it”? “It” is nothing — just a placeholder. Because we need a placeholder, because our grammar doesn’t let us simply say “Is rainy out today.”

When it comes to Ruby — or any programming language — the agent making things happen exists at a different level of abstraction. Who’s doing everything? Beginners in particular don’t know much about the interpreter “under the hood.” We might refer to it as “the program.” Or to simplify further, maybe even “the computer.”

Something invokes the #map method on an array object. Something passes a block to #map as an argument. Something evaluates, and returns, and mutates, and initializes, and reassigns.

We can develop a mastery-level understanding of the meaning of Ruby code without knowing the slightest thing about the invisible force that breathes life into the code behind the scenes.

And that’s what makes reading Ruby hard. Reading Ruby in English means trying to form every sentence without mentioning the very essential subject. The one doing all the passing and returning and invoking.

And since our subject is missing, it’s very easy to get twisted around trying to articulate coherent English sentences. A common workaround is to use “we”: “On line one, we are initializing the local variable “a” to reference…” This works. It does. The point is conveyed. And it’s an easy grammatical structure to grab, because it allows us to continue using our default active-voice sentences. But it’s far from perfect. If it works for you, roll with it. It didn’t work for me. It struck me as wordy, and repetitive, and vague, and a little messy.

Enter: passive voice. Yes, there was a point to the grammar lesson. Learning to use passive sentences to read Ruby is how you crack the code.

Phrases for Reading Ruby

Learn to reach for these verb forms and your sentences will fall into place.

Key Passive Verbs

Below are the main verbs you need to concisely read Ruby code in English. It’s important to note — using these only becomes easy when you learn to read right to left. In the examples listed below, these verbs are bolded for emphasis.

  • is invoked / is called
  • is initialized
  • is reassigned
  • is passed
  • is returned
  • is mutated
  • is referenced
  • is evaluated
  • is chained
  • is bound

Helpful Active Verbs

In some cases, active verbs can help. Know the cases. These are exceptions to the norm. In the examples below, I’ve italicized these for emphasis.

  • passes — a method passes an element into a block
  • returns — a block returns a value to a method
  • mutates — a method mutates the calling object
  • references — a variable references an object
  • binds — an object binds to a parameter

Most of the time, passive voice works equally well:

  • an element is passed into a block
  • a value is returned by a block
  • a calling object is mutated by a method
  • an object is referenced by a variable
  • an object is bound to a parameter

The active form of these verbs works because we have a subject we can point to that’s doing something. In these cases, we’re describing the functions or properties of the element (the block, method, variable, or parameter) rather than describing the evaluative process the code is going through.

Putting These Verbs to Work

Below are three examples of the type of code descriptions Launch School students practice explaining in preparation for the first written assessment.

Getting good at producing descriptions like these goes far beyond passing an assessment — it leaves you with a much deeper understanding of what a program is doing, hones your ability to think through your algorithm before you begin writing code, deepens your ability to read other people’s code, and makes the debugging process much easier.

Example 1

We have a string object (“earlobe”) and a method (#chars). We start on the right. And on the right is: #chars.

“The #chars method is called on a string object with value “earlobe.”

Note: #chars is still not doing anything. It’s having something done to it (it’s being read and executed by the interpreter). But using the passive verb form gives us the tools to form clear, concise sentences.

To compare: the active verb alternative I hear most often is: We are calling the chars method on the string object “earlobe.”

And if we do this:

Right to left. Same as before: “The chars method is called on a string object with value “earlobe.” Local variable array_of_letters_in_the_word_earlobe is initialized to reference the return value of the #chars method call.

We have three different elements (method call, string, variable) and we describe what they are and what they’re doing in order — that order just goes right to left.

Right to left reading using passive verbs makes it simple to explain even complex nested iterative structures containing multiple method calls and return values.

Example 2

Here’s an example of applying the passive verbs listed above to describe what happens to a string when it’s passed to a non-mutating method.

On line 6 local variable example_string is initialized to reference a string with value 'Today is Monday'. (Since a variable is made to point to an object, simple initialization and reassignment are both instances in which moving left to right makes sense. Something is happening to the variable, and the string is receiving that action.)

On line 8 the question method is invoked and the string object referenced by example_string is passed in as an argument. The question method is defined on lines 1 - 4. The string object passed in as an argument is bound to method parameter string.

On line 1, the #+ method is called on the string referenced by method local variable string . This method call is passed string object '?'as an argument. #+ is a nondestructive method -- when it's called, it creates and modifies a copy of the string it's called on.

Here, the method call concatenates '?' to the end of the string object referenced by string and returns the new string object 'Today is Monday?' . (Active verbs italicized -- here the method is actually doing something.) The method local variable string is reassigned to reference this new string object.

(Notice this description on line 2 is starting in the middle, focused on what is happening with the #+ method. The method is doing something, so that’s our starting point. Once that action on the right is complete, we then move left and describe the reassignment. Explaining the method call before the reassignment helps you avoid getting stuck when trying to describe what the reassignment is referencing.)

On line 3, the mutating #downcase! method is called on the object referenced by string. This method converts all letters in a string to their lowercase version. Since this method call is the last evaluated expression in the method, the method's return value is 'today is monday?'.

The method’s return value is a different string object than the object initially passed into the method as an argument. The non-mutating method call on line 2 creates a copy of the original string. Everything else that happens in the method happens to that copy, not the original string.

On line 10 the #puts method is invoked and passed the object referenced by local variable example_string . This line outputs the original string object, with no mutations - 'Today is Monday' .

This code demonstrates the concept of pass by value. When the argument is initially passed into the method, both example_string and string reference the same string object. However, on line 2 in the method's body a copy is created, and the code within the method only affects the copy, not the original object.

Example 3

One final example, this time looking at a nested iteration:

On line 1 the #select method is called on a nested array. A do...end block is passed as an argument to the #select method call. On each iteration, a subarray is passed into the block and is bound to block parameter arr .

On line 2, the #select method is called on the subarray referenced by block local variable arr . The inner #select is passed a do...end block as an argument. On each iteration, one subarray element is passed into the block and is bound to block parameter food .

On line 3, the #start_with? method is invoked on the string object referenced by block local variable food , and passed string object 'a' as an argument. On each iteration, this method call evaluates the calling object and returns a boolean: true if the string begins with 'a' , false if it doesn't.

#select works by selecting those objects for which the block iteration returns a truthy value and returning them in a new array. ( In this case, the inner #select will return ['apple'] and ignore 'kiwi' , which returns false . On the second iteration, it will again return ['artichoke'] and ignore 'broccoli' .)

The arrays returned by the inner #select become the block return values for the outer #select . Since each of these returned new arrays has a truthy value, each iteration of the outer #select block will return true. The entire subarray is selected and returned in a new nested array. On line 1 local variable selected_foods is initialized to reference the return value of this nested iteration.

On line 7 the #p method is invoked and passed the nested array referenced by selected_foods as an argument. This method call both outputs and returns a copy of the original nested array. The code demonstrates the importance of understanding how block return values are used to perform selection on collection objects.

Additional Tips

-Don’t repeat yourself. If you’re describing a block or an iterative structure (like each, select, map, or times), take a bird’s eye view. You’ll waste time and run in circles if you find yourself trying to describe each iteration. Instead step back and verbalize what the iterative structure as a whole is doing.

-Consider whether you’re describing actions being performed by the interpreter (and use passive voice) or by some part of the code (and use active voice). A good rule of thumb: if you can identify who’s responsible for the action, active voice works. If not, default to passive. (What reassigns a variable? Interpreter. Passive. What passes a return value back to a method? A block. Active works.)

-Focus on one method call at a time.

-Try to keep sentences short — one method call, assignment, etc., per sentence helps writing stay clear.

-Use terminology to demonstrate your knowledge, but don’t let it trap you. Saying, “The string object with value 'hello' referenced by block local variable string ..." the first time you encounter the object demonstrates you know how to describe that object. Feeling compelled to use that phrase every single time you refer back to it will make your writing lengthy, time-consuming to write, and harder to read. Once you've demonstrated your knowledge, calling it 'hello' is fine as long as the context is clear.

-Methods are king. Methods are doing all the heavy lifting, and the method should take precedence in descriptions. A method is called on a variable, and is passed an argument. As much as possible, orient sentences around describing what the method calls are doing — assignment, reassignment, and argument passing are often happening in response to the method call.

- Integrate descriptions as you go. If you look back at Example 3 above, you can see how explanation is woven is:

On line 1 the #select method is called on a nested array. A do...end block is passed as an argument to the #select method call. On each iteration, a subarray is passed into the the block and is bound to block parameter arr . On line 2, the #select method is called on the subarray referenced by block local variable arr...

Of these four sentences, three are reading the code. The bolded sentence is explanation dropped in at a convenient breaking point, before starting on the next section of code. Explanation can be incorporated like this, but if it’s easier you can also hold off and instead describe at the end.

Opting for the incorporated method helps keeps thoughts organized and leads to a shorter description, but it might take a little extra work at first to jump back and forth between reading code and explaining code.

Final Notes

If you want to break the rules of grammar, first learn the rules of grammar — Kurt Vonnegut

1. Code is written in a language. Embrace the idea of reading or translating that language.

2. Get comfortable with passive verbs. You don’t need a grammar primer. The verbs listed above are more than enough.

3. Learn to read right to left.

4. Follow the pattern of what happened, what was returned.

There’s no one right way to write, read or describe code — but there are wrong ways. These tips provide a helpful way to organize sentences to produce accurate code descriptions.

Half the challenge is understanding the challenge. Gaining an awareness of the grammatical constructs you’re working with helps you understand why attempts at phrasing code your mind understands clearly leaves your tongue twisted and stumbling.

Getting comfortable with explaining code, either aloud or in writing, eventually means relying on passive verbs. Rules — in grammar and elsewhere — can often be bent or broken entirely once you understand the purpose behind their structure, but plunging forward without that knowledge often brings thankless and frustrating results.

--

--