Using #in?
This is a work in progress from The Pipeline. It will likely change before it is published to the main listing of articles. It is currently at the "hidden" stage.
One of the active support extensions that I’ve grown to like (yes,
they do exist) is the Object#in?
method.
TODO: better intro
TODO: use a different word to refer to the grammatical object, as distinct from the programming concept.
Active And Passive Voice
English, both written and spoken, has the concept of active and passive voice.
Active voice is when the grammatical subject of the sentence is the agent: the person or thing which performs the action[^This is a simplified, not comprehensive, definition]. For example, “I knocked on the door” is written in active voice because the subject is “I”, and I am the one who knocks.
Conversely, passive voice is when the grammatical subject is the recipient: the person or thing being acted upon[^Again, this is a simplification]. A classic example of passive voice is the sentence “mistakes were made”, where the subject is “mistakes”. The mistakes are being acted upon (being made) by a deliberately-unspecified actor (usually a politician).
Active voice is generally preferable to passive voice. Both voices are grammatically correct and have particular use cases, but active voice is considered to be more direct and clear. There are a few possible reasons for this:
-
Placing the agent at the beginning of a sentence provides useful context that makes the rest of the sentence easier to mentally digest.
-
Native English speakers, or perhaps humans in general, might process sentences better if the agent comes before the action being taking.
-
Passive voice provides less information, because it allows the agent to be omitted from the sentence, unlike active voice where the agent is mandatory.
Whatever the case may be, readability is likely affected by the ordering of agent and action within a sentence.
Identifier Ordering In Code
There are similarities between active/passive voice and the ways that code can be written.
Consider the implementation of a function that takes two arguments, in an object-oriented language. There are generally three places where this could be implemented as a method: on the first argument, on the other argument, or on some other object. As an example, a method that chops wood with an axe could be designed in these three ways:
axe.chop(wood)
wood.chop(axe)
lumberjill.chop(wood, axe)
The chop
method is quite clearly the action, but the agent and the
recipient are not so clear. Is it best to consider the agent to be the
axe, or the lumberjill? Or can wood have agency, and be capable of
chopping itself? These might seem like silly questions, because most
English speakers will quickly arrive at an intuitive answer.
But we don’t need to look far to find real-world examples of all
three, in code. A string can find matches in itself
(string.match(regex)
), or a regular expression can find matches in a
string (regex.match(string)
), or a pattern-matching engine can find
matches in a string for a given pattern (engine.match(string,
regex)
). Intuition becomes blurry in the context of the ethereal
thoughtstuff that is software design[^This problem is more specific to
object-oriented design than it is to software design in general. Users
of Lisp and other functional languages will inevitably congratulate
themselves for their impeccable taste, while ignoring the larger point
about how humans repurpose their natural language faculties to
understand code, which may partially explain why these programming
languages have low rates of adoption.].
Viewing Code Through The Lens Of English
The most intuitive approach to chopping wood, in the context of
English and Ruby, is probably lumberjill.chop(wood, with: axe)
. The
method is called on the agent, the recipient is passed in as an
argument, and the axe is differentiated from the recipient using a
keyword argument.
In English, “with an axe” is an adjunct: an optional or structurally-dispensable part of the sentence that provides additional information. For example, “the lumberjill chopped wood” is a perfectly cromulent sentence without the addition of “with an axe”. Adjuncts align nicely with keyword arguments in Ruby, which are often used to pass additional “options” to a method.
But the end goal is not to make code look like English, or any other natural language.
This is the case for all functions of two arguments, implemented as a method. The method can exist on either of the arguments, or on some other object which takes both arguments.
The code that is closest to typical English is lumberjill.chop(wood,
with: axe)
. The subject, verb, and object are quite clear. Even
the adjunct axe
is differentiated from the object wood
by using a
keyword argument that is intuitive to English speakers.
Got questions? Comments? Milk?
Shoot an email to [email protected] or hit me up on Twitter (@tom_dalling).