getprogwithcloj.png

From Get Programming with Clojure by Yehonathan Sharvit

In this article, we’ll illustrate the uniqueness of the Clojure syntax by focusing on function calls, arithmetic expressions, and if expressions at a high level.


Take 37% off Get Programming with Clojure. Just enter fccsharvit into the discount code box at checkout at manning.com.


Clojure is a LISP dialect. Lisp is a family of computer programming languages with a long history and a distinctive, fully parenthesized prefix notation

As a LISP dialect, Clojure uses the LISP syntax and it’s fully parenthesized prefix notation. Let’s see what it means.

A fundamental difference in the order of the parentheses between the LISP syntax and the syntax of most other languages is that in other languages, like Java or ruby, we have different syntaxes for different parts of the language, yet in Clojure all parts of the language follows the same syntax: the syntax of an s-expression or a symbolic expression.

After reading this article, you’ll be able to:

  • Describe the order of the elements in a Clojure expression
  • Read function call expressions in Clojure
  • Read arithmetic expressions in Clojure
  • Read if expressions in Clojure

 Table 1.  This table summarizes the Clojure syntax for the expressions which are covered in this article.

Expression

Example

Java-like syntax

Function call

(foo 1 2)
foo(1, 2)

Arithmetic operation

(+ 2 3)
2 + 3

Arithmetic comparison

(> x 2)
 x > 2

If expression

(if (> x 2)
    3
    5)
if (x > 2) 
{
   return 3;
 } else 
{
   return 5;
}

Nested function call

(foo 1 
     (bar 2 3))
foo(1, bar(2, 3))

Nested arithmetic expression

(* (+ 2 3)
   (+ 5 7))
(2+3) + (5+7)

Function calls

In most non LISP-based languages, the function symbol is followed by parentheses and is inside the parentheses in a function call, and the function arguments are separated by commas.

Something like: foo(1, 2).

When the arguments are function calls, we have a nested function call like: foo(1, bar(2, 3)).

In Clojure, the function symbol is inside the parenthesis. It might feel a bit unnatural to you because you’re used to the other notation. You probably know that LISP has many parentheses.

If you think about it, you’ll see that this notation has the merit to be symmetric. It treats the function and its arguments in the same way: all go inside the parenthesis.

For a flat function call, the equivalent of foo(1, 2) is (foo 1 2).


Figure 1.  The structure of a function call illustrated by an example that involves a function named foo.


The syntactic rules for a function call in Clojure are:

  1. A function call is enclosed in opening and closing parenthesis
  2. The first element inside the parentheses is the name of the function to call
  3. Other elements inside the parentheses are the arguments to be passed to the function: the elements are separated by one or more whitespace characters. (Commas between arguments are optional in Clojure, usually we don’t use them.)
  4. When one of the arguments is itself an expression, it follows the same syntactic rules

DEFINITION Whitespace By whitespace, we mean any character which is used for spacing, and has an empty representation. In the context of Clojure, it means spaces, tabs and end of line characters. A proper choice of whitespace leads to an indentation of the code that facilitates the reading of the code.

For a nested function call, the Clojure equivalent of foo(1, bar(2, 3)) is (foo 1 (bar 2 3)).


Figure 2. 


Table 2. This table summarizes the translation between Java-style and Clojure syntax for flat and nested function calls

 

Java style

Clojure

foo(1,2)
(foo 1 2)
foo(1, bar(2, 3))
(foo 1 (bar 2 3))
foo(baz(4, 5), bar(2, 3))
(foo (baz 4 5) (bar 2 3))

 

As you can check by yourself, in the case of function calls (flat or nested), the number of opening and closing parenthesis is exactly the same in both syntaxes.

 

The higher number of parentheses in LISP and Clojure comes from the fact that this syntactic rule applies to all the expressions of the language: arithmetic operations function definitions, if statements etc.

 

Arithmetic expressions

 

Let’s take a look at what an arithmetic expression looks like in a Java-style language. The arithmetic operator syntax differs from the function call syntax. Instead of being prepended to its arguments, like in a function call, the operator symbol comes between the numbers like this:

 

  
 2 + 3
  

In Clojure, it’ll be (+ 2 3).

The + symbol is another function to be called. As such, it follows the syntax of the function calls that we saw in the previous section.


Figure 3. The structure of an addition with the + form illustrated by an example.


It’s important to notice that the + function in Clojure can receive more than two arguments which is handy and makes the arithmetic expressions shorter than in other languages in some cases.

For instance, 2 + 3 + 4 becomes (+ 2 3 4).

And a nested arithmetic expression like (2 *3) + (5 *7) is: (+ (* 2 3) (* 5 7)).

The same logic applies to arithmetic comparison operators. If we want to check whether the value of x is greater than two, we write (> x 2) instead of x > 2.

Table 3. This table summarizes the translation between Java-style and Clojure syntax for flat and nested arithmetic expressions

Java style

Clojure

2 + 3
(+ 2 3)
2 + 3 + 4
(+ 2 3 4)
(2*3) + (5*7)
(+ (* 2 3)
    (* 5 7))
 x > 2
(> x 2)

 if expressions

Now, let’s take a look at how an if statement looks in a Java-style language. Let’s write a piece of code that:

  • compares x with 2
  • Returns 3 when x is greater than 2
  • Returns 5 otherwise
  
 if(x > 2) {   return 3 } else {   return 5 }
  

Please take a moment to notice that this expression doesn’t follow at all the same syntax as the function call syntax. Let’s decompose this expression:

  • if(x>2)          — this part is similar to a function call
  • { return 3}   — here a new symbol appears: the curly braces
  • else               — a symbol for defining what to do when the condition doesn’t hold
  • {return 5}    — curly braces again

In java-style languages, the syntax of an if statement completely differs from the syntax of a function call. You’re probably used to it and you don’t notice it anymore.

In Clojure, the previous if statement is written as an if expression:

  
 (if (> x 2) 3 5)
  

Please take a moment to observe the structure of this if expression and notice that it looks similar to a function call. Indeed, in Clojure, if expressions follow the same syntactic rules as any other expressions in the language: an expression is made of an operator and optional arguments surrounded by parenthesis.

In the case of an if expression:

  1. The first argument is the condition
  2. The second argument’s the value of the whole expression in case the condition holds
  3. The third argument is the value of the whole expression in case the condition doesn’t hold.

We can make this expression more readable by indenting it. Recall that we can add as many whitespace characters as we want without altering the meaning of the expression. The expression becomes:

  
 (if (> x 2) 
     3
     5)
  

We aren’t going to formally define the indentation rules in Clojure. Hopefully, you’ll get used to them naturally by reading the article.

In Clojure, there’s no else keyword to introduce the value of the whole expression in case the condition doesn’t hold. It makes the code more concise and once you get used to it, it’s also more readable.

Like in many other languages, the else part of an if expression is optional: if we omit the third argument of an if expression it’s replaced by the default nil value. For instance, the value of the following if expression:

  
 (if (> 0 2)      3)
 is nil.
  

Now, if we combine all we’ve learned we can write a complex nested if expression

  
 (if (> x 2)
   3 
   (if (< x 10)
       (+ x  20)
       (+ x 10)))
  

The occurrence of three closing parentheses at the end of the expression is an example of what makes people say that in LISP there are too many parentheses.

In Java-style syntax, it’s:

  
 if(x > 2){     return 3   }
 else {
     if(x < 10){
       return x + 20
     } else {
       return x + 10
     }
   }
  

Indeed, in this case in Clojure, we have six opening and closing parentheses, yet in Java we have:

  • two opening and closing rounded parenthesis
  • three opening and closing curly braces

 Table 4.  This table summarizes the translation between Java-style and Clojure syntax for flat and nested if statements:

Java Clojure
if(x > 2) {   return 3 } else {   return 5
}
(if (> x 2)
    3
    5)
 if(x > 2){     return 3   } 
else {     if(x < 10){
       return x + 20
     } else {
       return x + 10
    }
  }
(if (> x 2)
  3 
  (if (< x 10)
      (+ x  20)
      (+ x 10)))

In Java-style languages we have fewer parenthesis because the arithmetic expressions aren’t required to be wrapped into parenthesis. Also, we have different kind of parentheses: rounded parentheses and curly braces.

Finally, the fact that the convention in Clojure is to close all the trailing parentheses on the same line gives the impression that there are too many parentheses.

My hope is that after having followed this article the Clojure syntax feels much more natural to you. And hopefully you’ll soon come to love it!

Summary 

In this article, we introduced the basics of Clojure syntax and how it differs from other languages. We saw that, unlike other languages where we have a different syntax for functions, operators and statements, in Clojure the same syntax is applied to all parts of the language. We explored examples of function calls, arithmetic expression and  if expressions.

This uniformity of the syntactic rules is what makes the community say that Clojure syntax is simple.

If you want to learn more about the book, check it out on liveBook here and see this slide deck.