DEV Community

Cover image for Demystifying the Long Arrow "Operator"
Basti Ortiz
Basti Ortiz

Posted on • Updated on

Demystifying the Long Arrow "Operator"

I recently stumbled upon some code that I found to be really interesting. It essentially iterates over a loop but with a twist. It uses the mysterious long arrow "operator".

const pets = ['Lucky', 'Sparkles', 'Presto', 'Fluffykins'];
let i = pets.length;

// A wild LONG ARROW OPERATOR appeared!
while (i --> 0) {
  console.log(pets[i]);
}

// 'Fluffykins'
// 'Presto'
// 'Sparkles'
// 'Lucky'
Enter fullscreen mode Exit fullscreen mode

What's interesting and unusual about this "operator" is the fact that it iterates over the pets array in reverse, as seen in the console output. It seems that writing i --> 0 is like counting down to 0. Lo and behold, it actually is counting down under the hood.

The Magic Behind the "Operator"

In this article so far, the use of quotation marks around the word "operator" has been no coincidence. The long arrow "operator" is not really an operator, so to speak. It's more accurately a combination of two operators.

The long arrow "operator" (-->) is just a combination of the postfix decrement operator (--) and the greater than operator (>).

Since JavaScript ignores whitespace most of the time, we can cleverly format our code in such a way that glues -- and > together into -->. Instead of saying x-- > 0, we can write x --> 0. Regardless of format, JavaScript will interpret the long arrow "operator" as two separate operators.

NOTE: If it seems strange that x-- can be used for numerical comparisons, I wrote an article recently about the nuances of the increment and decrement operators. You can read about it here.

// All of these _output_ the same thing to the console.
// EXAMPLE 1: Good ol' `while` loops
let a = 5;
while (a > 0) {
  a--;
  console.log(a);
}

// EXAMPLE 2: Good ol' `for` loops
for (let b = 4; b >= 0; b--) {
  console.log(b);
}

// EXAMPLE 3: Combination of two operators
let c = 5;
while (c-- > 0) {
  console.log(c);
}

// EXAMPLE 4: Long arrow "operator"
let d = 5;
while (d --> 0) {
  console.log(d);
}
Enter fullscreen mode Exit fullscreen mode

Don't ditch the loops

So there you have it. The mysterious long arrow "operator" is just a combination of two operators. I think it's a pretty nice way of reading code because of how analogous it is to the notation of limits in calculus.

With that said, here is a list of the many ways I would read x --> 0.

  • "as x approaches 0"
  • "x goes to 0"
  • "count down x to 0"
  • "decrement x until it reaches 0"
  • "subtract 1 from x until it reaches 0"

Although the long arrow "operator" looks nice to read, I wouldn't write my code with it. The code formatting is just too clever. At first glance, especially for someone new to the language, it does not seem intuitive at all. One can quickly search Google about some long arrow "operator" in JavaScript, Java, or C++ just to find out that there aren't many resources about it.

It's just not "beginner-friendly" enough, which is why I don't like it. One has to be aware of the return value of the postfix decrement operator to fully grasp why such code is even syntactically correct. Beginners should never bother with the intricacies of a programming language to learn it. Explicit is better than implicit, as they say.

Besides that, the long arrow "operator" acts like a countdown. As a consequence of this, it iterates on arrays in reverse, which may not exactly be a desired behavior in some cases.

To summarize, the long arrow "operator" is a clever way of formatting two different operators. Unless you (and your peers) are fully comfortable with reading long arrow notation or you just want to impress your friends with some strange syntax they have never seen before, it is better to stick with for loops for general-purpose iteration.

Latest comments (63)

Collapse
 
gsmoffln profile image
gsmoffln

Note that in every modern browser “the Robin Hood operator” is actually considered a start of a single – line comment if it occurs at the start of the line or is preceded only by whitespace.

So if you accidentally put an EOL after “x”, that code becomes illegal.

OTOH, it is possible to hide some malicious hidden/polyglot code behind the --> operator.

stackoverflow.com/questions/578004...

Collapse
 
somedood profile image
Basti Ortiz

Wowwwwww... This is just outrageous! As much as I love the simplicity of HTML (and the language itself), I absolutely hate the fact that it forced the JavaScript specification to accommodate for its strange code commenting syntax. In all my years of writing HTML, I personally never liked this <!-- --> way of commenting code.

Collapse
 
gsmoffln profile image
gsmoffln • Edited

Yes, all that overly tolerant HTML tag soup syntax with its complex parsing and fallback rules is inherently evil. The browser should not do any telepathy thinking and guessing on behalf of the programmer, especially in such an unsafe environment as public network computing with plain text communication channels.

That's why I prefer XHTML5 over HTML5, and always put my foreign-language code (CSS, scripts, templates, data blobs, or simply user-entered strings from a database) into well-formed <![CDATA[ ... ]]> sections.

On the other hand, we might as well think of Ecmascript and server-side languages as PHP as especially retrofitted to be mixed with a well-formed XML.

Once Javascript1.7 in Gecko had that E4X extension that actually allowed well-formed XML tags to be valid JS tokens (and first-class data types). Combine that with JS string template interpolation:

<script><![CDATA[
   var span = <span class="{class}">Today is: {new Date.toLocaleDateString()}</span>;
   document.querySelector(`div#output`).appendChild(span);
]]></script>

And you have a perfectly validatable polyglot grammar. As you see, the XHTML/JS/E4X parser contexts are (almost) completely isolated.

Unfortunately, E4X is dead in Gecko, but with its simplistic reincarnation in React as ESX you can get essentially the same.

The only problem is with lazy web developers. Tersiness of XML (hence, XHTML5 too) was never a design goal. So basically we only need to evangelize high quality validating editors. And to teach our students to use them properly, without counting characters.

Lastly, note that syntax and semantics of<!CDATA[ .... ]]> is specifically designed for embedding large unencoded character data verbatim. Contrary to XML/HTML TextNodes or attribute values, its contents are not supposed to be encoded or protected anyhow. It does not have any internal structure except the ending trigraph. It does not interfere with contents inside, as opposed to ordinary <script>, <style>, <pre>, <textarea>, <template> or similar tags. It does not leave junk unlike the <script>//<-- ... //--></script> hack.

The only highly unlikely problem remains: if you really need the actual string "]]>" inside your text block. It is not even syntactically possible nowhere in JS or CSS except comments or constant string literals, where there are many trivial options to work around. You can simply split the trigraph inside the string by means of JS/CSS or by exiting and reentering the CDATA regions. Even if you fail to notice the bug yourself, your editor will probably catch it because both the structure of your script and the XHTML will be severely broken.

Thread Thread
 
somedood profile image
Basti Ortiz

Wow, I never knew how much of hassle this was until you explained it to me. Thanks for the new information (at least for me)!

Collapse
 
gsmoffln profile image
gsmoffln • Edited

This is exactly why I hate such C-style syntactic sugar as pseudo-atomic “function plus side effect” operators. Especially their prefix versions “copy the original_value somewhere; copy it once again, increment and store back; use the old copy of original_value as the result”.

Consider for(;;i++) versus for(;;++i). It is not obvious at all whether the actual modification of i actually occurs at the start of the loop body (where it is written) or at the end. What about for(;i++>n;) / for(;++i>n;) ?

It is confusing at least, and seemingly has been created for the sole reason of brevity of loop expressions. Where other languages have “for(i : 0..n-1)...”, in a C-style language you should repeat yourself as in “for(i=0;i<n;i++)...”.

Otherwise why do we have ++ and --, but no !!, **, //, &&&&, ||||, <<<<, >>>>, >>>>>>?

That given, I still consider modify-and-assign-back operators (i+=1, i*=2, i>>=1, etc.) a very valuable syntactic shorthand for inc(i,1), shr(i,1) as in other languages e.g. Pascal, and yet they still have some ambiguity such as in “x^=y^=x^=y”. A novice might expect that ^= is an atomic operator and this expression is a perfectly legal XOR-swapping.

Unfortunately, the consensus among modern languages is exactly the opposite: every variable is loaded just once before evaluating the expression, and their temporary changes are lost.

Now consider a language such as C# where += is overloaded for a collection type as .add(value) or .addAll(collection). What would be the semantics of “col+=col+=col”?

I think that operations such as “increment and assign” should have been defined as statements, not expressions – i.e. to return a void value and be forbidden to be nested into larger expressions.

This way you could still write something along the lines of “while(i-=1,i>0)” without any ambiguities.

While the compilers allow such ambiguous compound expressions, I feel that they should be explicitly forbidden at least in code style guides.

As a side note, the = character is also the reason why I feel that the syntax of lambda expressions in ES6 ( x=>x+1 ) is much more confusing than in Java ( x->x+1 ). The double arrow intuitively implies a definition or an assignment back to the parameter list, while the single arrow reminds the mathematical notation of mapping.

Unfortunately in C++ and PHP the single arrow operator was already defined as a dereference of a struct member, so we have double arrows in C++ and, by copying, in ES6 (but frankly not in PHP7). Actually, in PHP the double arrow is exactly the definition and assignment of associative array's member (and mapping keys to values), and I don't see the reason PHP should introduce a shorthand syntax for anonymous functions (a sugar for function(){} syntax like in ES5).

Perhaps ES6 shouldn't have either – in my personal opinion, Firefox 3.6 had a better solution, that it allowed just to write a return expression instead of a function body (without curly braces and an explicit return keyword):

var f = function(x)x+1; [0,1,2].map(f); // returns [1,2,3]

You could define lambdas almost just as concisely but without any special keywords or operators.

This shows that not only novices, but experienced language designers too are prone to inventing unnecessary cryptic operators.

Collapse
 
somedood profile image
Basti Ortiz

I like your take on this. It's very thought-provoking. These operators truly are one of the sources of evil in C-like languages.

Collapse
 
helpermethod profile image
Oliver Weiler

Never combine post/pre increment/decrement with other operators. It makes code hard to understand and can lead to nasty bugs. That's why many langs like Python don't have them at all.

Collapse
 
lagsurfer profile image
Barney

I think its pretty dumb and extremely funny :D

Collapse
 
dance2die profile image
Sung M. Kim • Edited

"long arrow is a bad idea" seems to be a common theme in most of comments.

Knowing that it's a bad practice makes this post worth reading 😀

And love your avatar & the featured image😍

Collapse
 
somedood profile image
Basti Ortiz

Thanks! I'm glad you liked them (the post and my avatar).

People truly have strong opinions on best practices, don't they? 😁

Collapse
 
dance2die profile image
Sung M. Kim

You're welcome 😊

People truly have strong opinions on best practices, don't they?

Those opinions make'em heard and let us learn from each other 😛

Collapse
 
jrwren profile image
Jay R. Wren

I'm reasonably sure that prettier fixes this so that its not an issue.

Collapse
 
leob profile image
leob

Never saw it before, wouldn't recommend using it. Too much of a "wow this is a clever trick" thing which doesn't do much more than harm readability (not criticizing the author here, I think he's acknowledging exactly this).

Collapse
 
tehpsalmist profile image
Ben Steward

Looks beautiful with my font ligatures!

Kewl Kode

Oh, what's that? A reverse long-arrow operator? Read about it here!

Collapse
 
cecilelebleu profile image
Cécile Lebleu

You are evil.

Collapse
 
akashdeepsingh profile image
Akashdeep Singh • Edited

This article is a much needed resource for someone who might come across such usage and google it. I agree that it shouldn't be used because it relies on whitespace formatting and in fact, deceives the reader into thinking it's a single legitimate operator.

I suggest adding names of languages that accept this usage in the title? And perhaps a clarification in the beginning that the "long arrow operator" doesn't actually exist.

Collapse
 
somedood profile image
Basti Ortiz

I'm sure the comments section have clarified those facts strongly enough. 😂

Collapse
 
theodesp profile image
Theofanis Despoudis

That's why everyone should avoid increment (++) and decrement (--) operators

As in the Douglas Crockford's Javascript: The Good Parts, he writes:

++ and --
The ++ (increment) and -- (decrement) operators have been known to contribute to bad code by
encouraging excessive trickiness. They are second only to faulty architecture in enabling to viruses and other security menaces. There is a plusplus option [in JSLint] that prohibits the use of these operators.

Collapse
 
somedood profile image
Basti Ortiz

Definitely can't disagree with the Great Crockford.

Collapse
 
theodesp profile image
Theofanis Despoudis

Tha'ts why everyone should avoid increment (++) and decrement (--) operators:

Collapse
 
dimpiax profile image
Dmytro Pylypenko • Edited

In 12 years of the programming, I first hear about "long arrow" operator. It confuses beginners.
But you can do the same, in easy way:

let x = 2
while (x--) { 
  console.log(x)
} 
// 1
// 0
Collapse
 
okdewit profile image
Orian de Wit

Watch out with that notation though, it's best to only use it on integers declared as a constant.

let x = 2

// Wait, you have to pay taxes over your loops!
x *= 1.1

while (x--) { 
  console.log("Nothing can be said to be certain, except death and taxes")
} 
Collapse
 
dimpiax profile image
Dmytro Pylypenko

You need to know what you are doing with your code.

Thread Thread
 
okdewit profile image
Orian de Wit

Certainly! But with Javascript not having an int type, it can lead to unexpected bugs.

Sometimes the space between the declared variable and the loop fills up with other code, especially in a project that's not squeaky clean to begin with.
Or the variable was passed in as an argument to a function, and eventually someone calls the function with unexpected input.

Maybe a coworker wants to loop over something in batched chunks, and naively divides x by a chunkSize.

Assuming that x in while(x--) is an integer which will hit zero isn't necessarily bad, just something to be careful with.

Thread Thread
 
dimpiax profile image
Dmytro Pylypenko • Edited

It's true that it is dangerous.
I see a reason to use tools (approaches) in related situations.
Construction without zero only in the case, when you know what you do. For example, iterate through array elements from the end:

const arr = ["this", "is", "c", "h", "a", "r"]
let n = arr.length
while(n--) {
  const el = arr[n]
  ...
}

or count input value as a number. In this case, input can be any, the function must know the type and cast to it. If float – make it as int by parseInt or Math functions.

Collapse
 
somedood profile image
Basti Ortiz

Yup, that's definitely true. In cases when one has to decrement until any number other than 0, I think the long arrow "operator" has its... "uses".

let i = 10;
while (i --> 5) {
  console.log(i);
}
Collapse
 
dimpiax profile image
Dmytro Pylypenko

Nowadays decrement and increment are bad practice. Consequently operator "long arrow" will be disappeared, and it's good.

Thread Thread
 
cecilelebleu profile image
Cécile Lebleu

If you don’t mind me asking, why are decrement and increment considered bad practice? They seem relatively simple and straightforward.

Thread Thread
 
somedood profile image
Basti Ortiz

Honestly, I don't consider it bad practice myself. I agree with your sentiment. It's just that it's only bad practice in the context of the "long arrow operator".

I'll let others answer your original question for you. I'm sure it has turned up in one of the discussions at some point.

Thread Thread
 
dimpiax profile image
Dmytro Pylypenko

In whole context,

  1. It has some bad relation with compiler optimizations. Don’t know what we have nowadays.
  2. It’s not good for abstraction. I.e.: ‘value += iterateAmount’.
Collapse
 
mogery profile image
Gergő Móricz

TL;DR: x --> 0 = x-- > 0

Collapse
 
gsmoffln profile image
gsmoffln

You probably meant,
x --> 0 == x-- > 0?

No, no, and no.

--> is a recognized ES token in every popular browser. If it occurs at the start of a line, it is considered a single-line comment.

Collapse
 
somedood profile image
Basti Ortiz

YUP. Can never agree more. 😂

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
somedood profile image
Basti Ortiz • Edited

I believe I hit on that point pretty explicitly in the article. Somewhere around here, I think?

Collapse
 
andrerpena profile image
André Pena

This is a total click bait title. Dismistifying something that doesn't exist. 🤔

Collapse
 
somedood profile image
Basti Ortiz

Aren't all "mysteries"? 😉