Syntax and Native Data Types

Let's get our hands dirty with native data types and common operations.

Syntax and native data types#

Clojure affords developers the standard collection of data types. All data structures are immutable. All data structures are manipulated using interfaces. This allows for easy interoperability with the underlying host.

In this module, we'll learn about native data types and some common operations. We suggest that you start the Clojure REPL and try these out for yourself. You can start the REPL using the clj command:

Reading Clojure code#

If you don't have a background in LISP, Clojure code will feel unnatural at first. This is because the brackets are in a different position.

In general, when you see a Clojure form like:

In your mind, you should think of it as a list where the first element is an operation to execute and other elements are arguments to that operation. This operation can be a function, a macro, or something else.

type function#

The type function tells us the native data type of a value or variable. It can be used as follows:

1 can be a variable or any other data type.

nil#

Any data type can be nil. The actual implementation of nil is dependent on the host. For Clojure, it's Java's null, for ClojureScript it's JavaScript's null.

The logical value of nil is false.

You can determine if a variable is nil by using the nil? function. A function suffixed with a question mark ? is conventionally a predicate.

Boolean#

These represent logical true and false values. On the JVM these correspond to java.lang.boolean.TRUE and java.lang.boolean.FALSE respectively.

The functions true? and false? can be used to ascertain if a value is truthy or falsy.

Number#

Like all other data types, numbers are also just the host's implementation of numbers. However, the differences across hosts are ironed out. Leaving the implementation details aside, the following types of numbers are supported.

Long#

All numbers in Clojure are Long by default. You can check this in the REPL by using the type function.

Ratio#

Non-terminating decimals like 22/7 are stored as ratios. The data type for the ratio is clojure.lang.Ratio. This signifies that Ratio is a Clojure data type and not dependent on the host.

Clojure takes care of the implementation detail across hosts.

BigInt and BigDecimals#

In case a number is too large for the Long type, it can be suffixed with an N to signify clojure.lang.BigInt. For example 44N.

Numbers suffixed with M signify java.math.BigDecimal or the equivalent host implementation of Java's BigDecimal.

Clojure operations intelligently wrap and transform numbers into the correct data type. For example, if you divide 22 by 7 using the divide / function, you'll get a Ratio:

Did you notice how we nested two functions (type and /) together?

Common numerical functions#

Since Clojure has no concept of syntax, numerical operations that you expect from a language are implemented as functions.

For computation, we have +, -, *, /, min, max, quot, and rem.

Algebraic functions can take more than one argument:

The core also ships with the functions inc and dec which add or subtract 1 to a number respectively:

Common comparison functions are also baked into the core. These are ==, >, <, >=, <=, zero?, pos?, neg?.

Like arithmetic operators, some comparison operators also take more than one argument:

The < function above expects the sequence of arguments to be in increasing order. The same is true for other inequality functions.

String#

Strings are denoted with double quotes "like this".

We already know the println function. Some other common string functions are:

  • prn : Like println but doesn't include a terminating newline

  • pr-str: Prints the given string and returns it, unlike prn which returns nil

  • str: Can be used to coerce a value to a string. Can also be used to join multiple strings

  • string?: Determines if a variable is string or not