Clojure Basics - thelastmile/FreeCodeCamp GitHub Wiki
Clojure The Basics
Getting Started
Before we begin, you may want to install Clojure and Leiningen (which is a tool for managing projects in Clojure). This will let you run Clojure in the command line using a REPL (Read-Evaluate-Print-Loop).
Defining Variables
The bread and butter of any programming language are variables and functions. Let's define a variable!
(def our-string "Hello world!")
Easy peasy. That piece of code uses the def
macro to associate a string ("Hello world!"
) to a symbol (our-string
). We could also have defined a number, such as 1
or 1.1
, a character, such as \a
or \Z
, or something more complicated like a list or a regular expression (uuuugh).
Note that our code is inside parentheses, like a list, because everything in a Lisp is a list! (That will be very important when we start talking about macros.)
Defining Functions
Now, let's define a function!
(defn hello-world [] (println our-string))
This is a bit more complex. Like def
, it uses a macro (defn
) to create a variable - although this time, that variable is a function. The empty vector (a vector is a type of list -- think of it like an array) after hello-world
defines the arguments to that function -- in this case, we don't have any. The code after that is what the function does. It evaluates our-string
, which is equal to "Hello world!"
, and prints it to the console. Let's run it!
(hello-world)
; => Hello world!
; nil
You could also write this:
(def hello-world (fn [] (println our-string)))
defn
is just a shortcut to help keep your code concise. (defn ...)
and (def ... (fn ...))
are the same in practice.
Parameters
Well, that was nice, but it wasn't really exciting. Let's try a function with parameters. How about one that adds 3 numbers?
(defn add [x y z] (+ x y z))
(add 1 2 3)
; => 6
...Hold on. (+ x y z)
? That looks funny. Well, Lisps are written using "prefix notation", which means that the function always comes first. Since all mathematical operators in Lisp (+ - * /
) are just functions, they also come before their arguments (in this case, x y z
).
You'll notice that our vector has some stuff in it now: [x y z]
! Whenever a function has parameters, you define them in that vector next to the function's name.
Destructuring
A great feature about arguments in Clojure is destructuring. It allows you to 'pull' elements out of a list.
(defn add [[x y] z] (+ x y z))
(add [1 2] 3)
; => 6
:rocket: IDEOne it!
The arguments to that function are a collection ([x y]
) and a number (z
). We can use destructuring to pull the first and second elements out of the list, and call them x
and y
.
Functions with any number of parameters
You can also define a function with an arbitrary number of arguments using &
.
(defn demonstrate-rest [first & rest]
(println first)
(println rest))
(demonstrate-rest 1 "foo" ["bar" 22])
; => 1
; ("foo" ["bar" 22])
; nil
:rocket: IDEOne it!
As you can see, using &
separated our function's arguments into one variable called first
and a list of variables called rest
. This means that our function could have any number of arguments!
Returning
You may have noticed some odd things. Whenever we use println
, it seems like nil
is showing up in our output. Furthermore, our add
function returns 6
, but we never told it to return anything.
In Clojure, returns are 'implicit'. If you've used Ruby, you're probably familiar with this concept. Rather than telling our function to return something, it evaluates all the code inside its body, and returns the result. Our add
function, for example, evaluates (+ x y z)
, and then returns that result.
The reason why our functions that use println
output nil
is because println
evaluates to nil
. (nil
is like null
or None
-- it represents nothing.)
:point_left: Previous | :book: Home :book: | Next :point_right: |
---|---|---|
Summary | Table of Contents | Conditionals |