Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Clojure Basics for Java Developers

Clojure has been reaching peak hype in recent months. Check out these top notch Clojure examples and how the syntax differs from standard Java.

· Tutorial
Save
14.95K Views

At university, I didn't have Lisp (or any other functional language) classes. Then I started working as a Java developer and didn't have much contact with functional programming. At that time when I saw a piece of code like this:

(defn square [x]
    (* x x))

(deftest square-test
    (is (= 0 (square 0)))
    (is (= 1 (square -1)))
    (is (= 1 (square 1))))

I thought that you would have to be mad to use it. However, since functional programming has become more and more popular, I decided to give it a try and changed my mind (or became mad). In this post, I will try to share with you some of Clojure's (Lisp for the JVM) beauty.

Note: I'm still learning the language, so I might be oversimplifying things. If you're a seasoned Clojure programmer, you risk a heart attack reading this. :)

Hello World!

(println "Hello world!")

This is a "Hello World" program in Clojure (using REPL). The syntax is pretty basic:

  • Everything goes in parens:
    (...)

  • Function name goes first:
    (function-name ...)

  • Then arguments, if there are any:
    (function-name arguments)

The square function, defined in the first listing, would be called like this:

(square 2)

As you can see, there aren't more parens in Clojure than there are in Java, you just move the paren to the left!

Java:                 Clojure
square(2);            (square 2)
square(square(2));    (square (square 2))

Math Operations

Math operations like + or * also go first in the expression. Actually, these symbols represent Clojure functions. It might take a while to get used to it.

(+ 2 2)
(- 2 2)
(* 2 2)
(/ 2 2)

Actually, this notation isn't any harder than the "normal" one — just remember that operation goes first.

Java:             Clojure:
2 + 2             (+ 2 2)
square(2);        (square 2)

Defining a Function

To define a function we use the defn macro.

  1. We call defn:
    (defn ...)

  2. Then we declare function's name:
    (defn function-name ...)

  3. Then we declare the parameters in square brackets:
    (defn function-name [param] ...)

  4. Finally we put the body:
    (defn function-name [param1 param2]
        expression1
        expression2)

Last expression in the body acts as a return statement.

Java:                             Clojure:
int f() { return 1; }             (defn f [] 1)
int g(int i) { return i; }        (defn g [i] i)

Now the square function should be easy to understand. We take an argument x and return (* x x). Piece of cake!

Conditions

There is an if construct (special form) in Clojure, but it works a bit differently than the Java keyword. It works the same as Java's ternary operator — it returns one of two values based on a condition. Here's the usage:

  1. We call if:
    (if ...)

  2. Then we enter a condition:
    (if condition ...)

  3. Then goes the value to be returned when the condition is true:
    (if condition then)

  4. Lastly, we can put a value to be returned when the condition is false:
    (if condition then else)

An example might help a lot in understanding:

(if (> x 0) "positive" "zero or negative")
(if (< x 0) "negative" "zero or positive")

If we want to chain conditions, like if-else in Java, we should use the cond macro. It's syntax looks like this:

  1. Macro call:
    (cond ...)

  2. List of conditions with associated expressions:
    (cond
        (< x 0) "negative"
        (> x 0) "positive")

  3. Optional "else" expression:
    (cond
        (< x 0) "negative"
        (> x 0) "positive"
        :else "zero")

Solving a Real Problem

Enough learning for today. It's time to save the world by tackling the very famous "Fizz buzz" problem.

We'll start by defining a function taking one argument and returning it:

(defn fizzbuzz [x] x)

This solves problems for non-divisible numbers. Let's make a "fizz":

(defn fizzbuzz [x]
    (if (= x 3) "fizz" x))

That's a naive solution, because it doesn't work for 6, 9 etc., but we'll solve that later. Let's add a buzz:

(defn fizzbuzz [x]
    (cond
        (= x 3) "fizz"
        (= x 5) "buzz"
        :else x))

"Buzz" is there. With cond in place, adding "fizzbuzz" is trivial:

(defn fizzbuzz [x]
    (cond
        (= x 3) "fizz"
        (= x 5) "buzz"
        (= x 15) "fizzbuzz"
        :else x))

Perfect. It's high time to make the "fizz", "buzz", and "fizzbuzz" work for other numbers than 3, 5, and 15. We'll use the mod function for this:

(defn fizzbuzz [x]
    (cond
        (= 0 (mod x 15)) "fizzbuzz"
        (= 0 (mod x 3)) "fizz"
        (= 0 (mod x 5)) "buzz"
        :else x))

Notice, we had to move the "fizzbuzz" case up, because of the "fizz" and "buzz" conditions. That's it, world saved!

Conclusion

Despite the first impression, Clojure is a pretty simple language. Main constructs like functions, math operations or conditions are pretty similar to other languages. What's important, the language is capable of solving super-complicated problems like "Fizz buzz". I hope you had some fun!

Extra Watching

Published at DZone with permission of Grzegorz Ziemoński, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.


Comments
X