The classic approach of learning a new language is to start with syntax, data types, arithmetics and other primitives. In contrast, we will start with functions and pick up the necessary data-related components as we go.
This is how to call a function in Clojure:
range is the function's name, and
10 is the argument. Clojure is a dialect of LISP. All its structures are so-called S-expressions. Sometimes they are also called forms. Everything is wrapped in parens: function calls, function definitions, conditionals, loops, etc.
range is a function from
clojure.core, which, essentially, means that it's built in and comes with the language. When called with a single argument,
range returns a lazy sequence of numbers from 0 to the passed value, exclusively. (We'll talk about laziness later. Not, not because I'm lazy. I mean, not just because of that…)
To demonstrate a running Clojure code, we'll be using a REPL — an interactive shell-like interface to your Clojure programs. REPL stands for "Read, Evaluate, Print, Loop". The easiest way to play with Clojure code and a REPL is to open a REPL.it session.
At some point you should install Clojure locally (you need to install Java JDK first). Once installed, run
clj in the console. REPL.it website is handy, but your local environment will be faster and more comfortable to work with. Also, REPL.it's behavior makes it somewhat hard to quickly type in the Clojure REPL.
We can see that the sequence is another S-expression, which looks indistinguishable from a function call, as if
0 was a function's name, followed by nine numeric arguments. Keep this in mind, we'll get back to this mystery later.
Functions in Clojure may have different arities — the amount of parameters.
range has 4 arities: 0, 1, 2 and 3. Here are non-zero arities demonstrated. Try to guess what 2nd and 3rd arguments mean:
user=> (range 10) (0 1 2 3 4 5 6 7 8 9) user=> (range 3 10) (3 4 5 6 7 8 9) user=> (range 3 10 2) (3 5 7 9) user=> (range 3 10 3) (3 6 9)
When there's one argument, it corresponds to the
end value. When there are two arguments, they correspond to
end. The third argument specifies
step amount. If you open documentation about
range, you'll see this:
Usage: (range) (range end) (range start end) (range start end step) Returns a lazy seq of nums from start (inclusive) to end (exclusive), by step, where start defaults to 0, step to 1, and end to infinity. When step is equal to 0, returns an infinite sequence of start. When start is equal to end, returns empty list.
Everything in Clojure is an expression, so you can pass things around. Let's call another function and pass the result of
range to it:
user=> (first (range 10)) 0
first takes a sequence and returns its first element. There're also
user=> (second (range 3 14 2)) 5 user=> (last (range 3 14 2)) 13
To get an arbitrary element, use
nth, which takes 2 arguments — sequence and index:
user=> (nth (range 3 14 2) 4) 11
In case this feels confusing, here's how the same line of code would've looked in a C-like language:
nth(range(3, 14, 2), 4)
Use your local
clj, or use the REPL above, or open a new one in a separate window. Feel free to write in the editor itself and push the "play" button, or simply use the dark, interactive panel. You can drag the divider between the editor and interactive panel to resize those areas.
Exercise 1. Call
range and pass two arguments to it:
- Last value of
(34 67 18 299 13)
Of course, the point is not to just put number 13, but retrieve it programmatiaclly.
When creating lists manually, you need to put
' in front of it so that Clojure knows it's a list and not a function call. Example:
(second '(34 67 18 299 13))
Exercise 2. Multiplication and other arithmetic operations in Clojure are just functions
range and pass it one argument:
- the multiplication of the second and the last element of
(3 2 14 29 8 18 12)
Exercise 3. Call
range and pass such arguments so that the returned result is
(4 13 22 31 40 49).