A long time ago in a galaxy far, far away (not really) I started learning and writing Ruby professionally by diving in to an existing Rails project. Learning in a âtrial by fireâ sort of setting with other developers more experienced with a language is a great way to pick something up very quickly. I remember the first time coming across something similar to the following:
def something
@something ||= Something.new
end
This is probably the most basic and ubiquitous form of memoization in Ruby. In this case, I was told that with the combination of the
||=
operator and Rubyâs implicit return
this means:
Assign
@something
to a new Something object if it isnât already initialized and return it, otherwise return the preexisting value of@something
Itâs a simplistic answer but a sufficient one to tell a newcomer in the middle of a project. Having experience in other
languages, I thought to myself Ah! Rubyâs null coalescing operator! I wondered about it but it never really occurred
to me why the syntax of such an operator was the double pipe (||
). Shrug, move on.
After writing Ruby for a quite a while and having written various versions of this same pattern many, many times, I
was recently burned by ||=
. I had to very quickly come up with a toggle in a class and came up with this:
def some_toggle
@some_toggle ||= true
end
Note that not only is this a dumb use for using this memoization pattern (there are many ways to create such a toggle with a default value of true - donât use this one that I rushed to), it also doesnât work. After a very short investigation of the issue and actually taking a moment to rediscover this operator myself, I quickly realized my (perhaps obvious) misunderstanding.
True Null Coalescing Operators
A true null coalescing operator is âa binary operator that is part of the syntax for a basic conditional expressionâ where you can both specify a value to be evaluated which is returned if not null and a value to be returned if the first value is null. That mouthful can be seen in this pseudo-code:
/* Given two arguments... */
if $first_argument is NOT NULL
return $first_argument
else
return $second_argument
âNOT NULLâ could also just mean âundefinedâ. Some programming languages donât have an implicit notion of NULL. Many languages implement null coalescing operators. Here are just a few examples:
Perlâs version is //
my $greeting = $more_personal_greeting // "Hello.";
In C#, the operator is ??
string greeting = morePersonalGreeting ?? "Hello.";
Swift takes a cue from C# and also has ??
var greeting: String = morePersonalGreeting ?? "Hello."
From PHP5 and on, you can leave out the middle part of a ternary operator to create a binary one.
// Use a full ternary expression...
$greeting = $more_personal_greeting ? $more_personal_greeting : "Hello.";
// Or omit the 'if true' portion
$greeting = $more_personal_greeting ?: "Hello.";
Apparently future versions of PHP might have a true ??
operator like C# and Swift.
Groovy took PHPâs thunder and actually made a separate operator out of ?:
, calling it the âElvis operatorâ. Look at it like an emoji. See Elvisâs hair curl?
def greeting = morePersonalGreeting ?: "Hello."
Of course, all of these examples are null coalescing without implicit assignment; Weâre assigning $greeting
based on
the existence of $more_personal_greeting
. In Perl (at least) thereâs a variation on //
that acts and looks even more like
Rubyâs ||=
where assigning and checking $greeting
is done implicitly:
my $greeting //= "Hello.";
$greeting
is assigned to $greeting
if itâs defined (no change) or "Hello."
otherwise.
Rubyâs Conditional Assignments
Although itâs often used like it, Rubyâs ||=
is not a null coalescing operator but a conditional assignment operator.
In my quickly written Ruby example, @some_toggle ||= true
always returns true, even if @some_toggle
was previously
assigned to false
.
The reason is because this operator, âOr Equalsâ, coalesces false
as well as nil
. In Ruby anything that isnât false
or nil
is truthy.
âOrEqualsâ is a shortcut for writing the following:
# Generally
a || a = b
# Our example
@some_toggle || @some_toggle = true
Oh. So thatâs why the syntax is âdouble pipe equalsâ, ||=
. All this operator does is take Rubyâs easy ||
logic
operator and combine it with the assignment operator =
. In hindsight - after having a lot more experience with
Ruby logic and logical operators in general - it makes perfect sense. Itâs not wrong, itâs just not a true null
coalescing assignment operator. Donât fall victim!
A Ruby version of what weâre trying to do could look like this:
def some_toggle
@some_toggle = defined?(@some_toggle) ? @some_toggle : true
end
# Though even this isn't exactly the same - @some_toggle is _always_
# assigned (potentially to itself) in this case
Lastly, saying â||=
is a conditional assignment operatorâ is only a subset of the larger
truth. The mix of various operators and assignment is simply referred to as abbreviated assignment. Youâve probably
seen this in the form of +=
to increment a counter or add to a string. In fact, you can mix any of the following
operators in the pattern <operator>=
: +, -, *, /, %, **, &, &&, |, ||, ^, <<, >>
.