How much do you like sugar ?

Benoit Callebaut
6 min readMay 1, 2022
Photo by Amit Lahav on Unsplash

Everybody love sugar. It is written deep down in our brain. And every software developer is lazy at least a little bit. So you use programming languages instead of burning bits directly in your computer. (It did this don’t laugh)

Definition

Let’s stick to the Wikipedia definition

In computer science, syntactic sugar is syntax within a programming language that is designed to make things easier to read or to express. It makes the language “sweeter” for human use: things can be expressed more clearly, more concisely, or in an alternative style that some may prefer.

Maya Shaving came up with a nice trick to remember the definition:

  • Simplicity
  • Usability
  • Good code
  • Alternative
  • Readability

But the most important keyword to remember is productivity.

If your code is shorter, easier to read, write and review, you are finally more productive… or not. Lets dig into that.

What is a programming language ?

Every “modern” programming language share two main characteristics:

  • Turing complete : Any program can be simulated by a Turing machine.
  • Context free: The interpretation of a statement in the language is independent of the previous statements. It can be described by a formal grammar.

Sound complicated ? Well not that much.

The Turing machine

The Turing machine as a mathematical model of computation devised by Alan Turing. A detailed explanation can be found on the Wikipedia page but for the sake of comprehension we will use more “modern” terminology.

A computer process data ( “The strip of tape”) by executing a sequence of instructions(“table of rules”) coming from a finite set of possible instructions defined by the language’s grammar.

Example (in BASIC)

10 PRINT "Hello, World!"

The instruction is “PRINT” which print a text on the console. and the data is “Hello, World” stored somewhere in memory.

Context Free grammar

A formal grammar can be represented as a set of rules. And a formal grammar is context free when the same set of rules can always be used. That become tricky.

The best counter example of a context free grammar is most spoken languages liker French or English.

For example take the word “can”. Can you tell me if this can is a noun or a verb in this sentence. Well it is both at the same time like Schrodinger’s cat.

Depending on the previous words (“terminals”), it is a noun (second occurrence) and a verb (first occurrence). In Natural Language Processing terms, you cannot identify the POS (Part of Speech) classification of “can” without knowing its context

Calculator grammar in ANTLR (shortened)

equation : expression (EQ | GT | LT) expression ;

expression : multExpr ((PLUS | MINUS) multExpr)* ;

multExp : powExpr ((TIMES | DIV) powExpr)* ;

powExpr : number (POW number)* ;

The following text : 17 = 5 + 4 *3 can be processed by a parser implementing this grammar and will generate the following parser tree :

Concrete Parse Tree for the equation : 17 = 5 + 4 * 3

Complex isn’t it ? We start with 5 symbols (17,=,5,+,4,*, 3) and we end up with 17 nodes.

Well some of them are useless. guess which ones. The blue ones are obviously useless (although not totally useless). The green ones could be simplified.

In this case the tree would look like the following:

Abstract Syntax Tree for the equation : 17 = 5 + 4 * 3

A second interesting point is that you can now execute that tree to get the result. But how ?

Well, use a stack.

Stack corresponding to the equation 17 = 5 + 4 * 3 with execution steps

You start with the stacked symbols (terminals and productions ) and at each step you “reduce” the elements on the stack.

The symbolic program looks like:

  • multiply 4 by 3 and push the result
  • add 5 to the result
  • test that the result is equal to 17.

In a more “assembly language” style you get:

POP
POP
ADD
POP
ADD
POP
TEST

Which is exactly what you old programmable HP calculator did using the Reverse Polish Notation (RPN).

Back to our Syntactic Sugar

As you have seen in the previous example, a program is first converted in a parse tree and this parse tree is executed after some transformations.

In the last example when going from the concrete syntax tree to to abstract syntax tree we removed useless nodes and replaced them by more meaningful ones.

But what if instead we replaced a single node by a whole sub-tree ?

Well, that is exactly what syntactic sugar is all about : defining in the language’s grammar new rules (productions) containing only the meaningful elements and let our compiler (or interpreter) replace the generated nodes produced by these rules by the ones that would have been generated by the rules “replaced” by our new rules.

But there is a caveat : You must modify your compiler (or interpreter).

Well one language went around it : C.

Make your own syntactic sugar

Modifying a compiler is a really complex task. And even more complex if you don’t have tools like ANTLR which generate the parser for you automatically.

So why not instead preprocessing your code containing your syntactic sugar and let generate code without syntactic sugar ? That is exactly the option taken by the C/C++ pre-processor.

Engineers at the Bell Labs defined around 1973 a new “language” that could be embedded in normal C code and “pre-processed” to replace it with actual code. The major features of this language are:

  • Inclusion of external file’s content : #include
  • Textual expansion of values : #define
  • Parametrized macros : A macro is expanded like a normal #define but can contain parameters that are replaced during macro expansion
  • Conditions : The preprocessed macros can be disabled or enabled depending on the value of “pre-processor variables”

Risks of diabete and after taste

Why isn’t the C pre-processor not used by Python, Java or JavaScript ?

Are there downsides to using pre-processors that could explain the absence of the C pre-processor in another languages?

Pre-processors still exists albeit in the shadows. In Java, there are still the annotations which are basically a sort of pre-processing functionality embedded directly in the compiler.

The main drawback of the C pre-processor is that it is doing textual replacement. The pre-processor is unaware of the C language. It has no clue if the generated code is valid C code or not.

Even worse, while the initial idea is to make your code easier to read or shorter, it has been widely used to make it more cross-platform/universal. The downside of making your code more universal is that you make it also less readable, which is the total opposite of syntactic sugar and make your code taste bad.

There are other issues with (custom) syntactic sugar:

  • Language complexity : more syntactic sugar means a more complex language. The C++ language specification is more than 5 times bigger than the C language specification but is it 5 times better ?
  • Hidden side effects: Not every syntactic sugar feature is useful in every case. Sometimes, such a construct limits you in term of flexibility. With the foreach construct, you cannot access the counter. So how do you construct a path from a list of path elements without having a trailing path separator ?
  • Poorly known constructs: A construct used only a few times in your program is likely to be poorly understood by you and the reviewers and is thus prone to introduce mistakes, and make code refactoring harder.

Conclusion

Syntactic sugar has a long history. From a smart trick to make you life easier to a complex set of features (C pre-processor), it is another tool that can be used for good or evil.

Use is wisely and you improve your productivity, use it too much and you ends up screwing up yourself.

Sometimes, the best syntactic sugar is simply to use another programming language better suited for you needs. But this opens up another Pandora’s box and is the subject of another article

--

--

Benoit Callebaut
0 Followers

Musician and embedded system engineer by passion, I value perfection and the efforts to achieve it.