Welcome, young lisper. I see you have heard the summons from the mystical forces. Prepare yourself for a journey like no other. Your view of programming shall change, and change for the better. You must shed your past beliefs and practices. This is a new path, one that bears little resemblance to the paths you have trodden before. And that is a good thing.
Initiation rites
You have two options - either use an online one-click setup or bite the bullet and install clojure and other tools on your local machine.
I recommend the latter option, since you will have to do it at some point.
But I am not without sympathy.
Setup can be a pain and can be discouraging.
Follow the instructions on
calva online for a zero-install approach to getting started with clojure.
If you have chosen the traditional route, you will need three things to get started.
- Java
- Clojure
- A text editor with clojure extensions.
1. Java
You need java to run clojure. So download that if you don’t have java on your system download java.
Check that java is installed with this command.
java --version
you need java 11 or higher So please upgrade if your java version is lower than 11.
2. Clojure
Most clojurists use leiningen to manage their clojure projects. Let’s follow their tradition. You can either head to the leiningen page and follow their instructions. download leiningen or use a package manager.
Ubuntu
sudo apt-get install leiningen
Mac
brew install leiningen
Windows
With chocolatey
choco install lein
With scoop
scoop install leiningen
Test your install
lein version
if you get something like this then you’re good to go
Leiningen 2.9.6 on Java 16.0.1 OpenJDK 64-Bit Server VM
3. Text editor setup (optional)
Follow the intructions of each extension on how to get up and running. It’s nice to do the setup now, but for this blog post we won’t need the specific extensions, so you can skip this for now.
VSCode
I personally use vscode a lot, and sometimes intellij. I recommend this if you don’t know emacs or vim
Calva is a beautiful extension that you can install in vscode. It’s all you need.
Intellij
Cursive is an extension you can use. It does have a free license for non-commercial use
Emacs
Cider is a highly recommended package to use.
If you use some other text editor, you can google around. I obviously can’t cover every editor.
First steps
Despite what you may have heard, lisp is not hard.
In fact, it is a very simple language, with simple rules.
Clojure is a lisp dialect(programming languages have dialects? don’t worry about that right now), so don’t get confused, because I use those terms interchangeably.
Clojure is easy, you’ll learn about 80% of the syntax in the next two minutes.
The repl
open up a terminal/bash shell, and type lein repl
.
$ lein repl
you should see something like this.
user=>
This is the REPL (read-eval-print-loop). We will type our code in the repl. The repl reads the input, evaluates it, and prints out the answer.
user=> 1
1
user=> 2
2
Alright, let’s get started!
Calling functions
Let’s say we have a function named add
in python that adds two numbers.
This is what the function call looks like.
>>> add(1,2)
3
all you have to do to make this valid clojure code is, move (
to the left of add
, like this
(add 1 2)
That’s valid clojure code. All you did was move a parenthesis and remove the comma. In fact, you can keep the comma and it will still be valid (but don’t, it’s not recommended)
If you tried typing that in the repl you probably got an error like this.
user=> (add 1 2)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: add in this context
That’s cause add
is not the name of a function.
replace add
with +
and try.
user=> (+ 1 2)
3
YEEET!
But that probably looks odd to you.
You’re used to 1+2
not + 1 2
.
But tell me, doesn’t add(1,2)
or (add 1 2)
look proper?
why should you discriminate between add
and +
?
Both are functions.
In fact, other languages discriminate between add
and +
, but not lisp.
Here’s one benefit to writing (+ 1 2)
.
If you had to add 10 numbers in another lanuage you would have to write something like
>>> 1+2+3+4+5+6+7+8+9+10
55
the same thing in lisp would be
user=> (+ 1 2 3 4 5 6 7 8 9 10)
55
Now, isn’t that rad?!
Defining functions
let’s fix this error.
user=> (add 1 2)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: add in this context
When Clojure says it’s unable to resolve the symbol, it means you haven’t defined that thing (or imported it).
Let’s define add
to fix this error.
(defn add [a b]
(+ a b))
okay, there are a couple of things going on here.
For one, there is a second pair of parentheses!
let’s start with stuff we know.(+ a b)
is syntax to add a
and b
where
a
and b
are two variables that will hold two numbers.
The [a b]
in (defn add [a b]
are the arguments that the function expects.
In this case it is two.
defn
is what you use to define functions, this is very similar
to python or ruby’s def
.
This function definition is not that different from a python definition.
; in Clojure ; # in python
(defn add [a b] ; def add(a,b):
(+ a b)) ; return a+b
Also, comments in clojure start with ;
.
I’m trying to really drive home the fact that lisp syntax is not that different (at least in the initial stages).
paste the add
definition in your repl, and try calling it a few times.
user=> (defn add [a b]
#_=> (+ a b))
user=> (add 1 2)
3
user=> (add 7 8)
15
Why clojure does not have return
.
As you saw in python’s definition of add, you had to explicitly
tell python to return a value.
A lot of languages have this, java, C, C++, ruby,
why doesn’t clojure?
In clojure (or lisp) if you place something in parenthesis,
it’s like saying “yo lisp, do me a fava', find the solution to the thing inside the parenthesis and give me the answer”.
In programming jargon, everything inside a parenthesis is an expression, and lisp evaluates the expression and returns the value.
If you don’t know what an expression is, a simple way to think of it is any line of code that returns a value.
1+1
is an expression and so is add(1,2)
.
Lisp only has expressions (no statements), so in lisp every ( )
returns a value.
Even println
in clojure returns a value, it is nil
.
user=> (println "hello")
hello
nil
And that’s why you didn’t have to explicitly tell add
to return
the result.
(defn add [a b]
(+ a b))
The anatomy of a named function
add
is a named function because it has a name “add”, simple, right?
There are unnamed functions too, but we won’t get to that just yet.
A named function consists of three parts, a name, arguments, and a function body.
; name arguments
; --- ----
(defn add [a,b]
; body
; -------
(+ a b))
Everything that comes after the arguments is the function body. The body can be as long as you want.
What if a function has multiple expression in its body?
Let’s look at a function that has multiple expressions in it’s function body.
(defn do-math [a b]
(+ a b)
(- a b)
(* a b))
This function has three expressions
(+ a b)
(- a b)
(* a b)
Which one will be returned?
If you guessed (* a b)
then you’d be right, this is because
lisp always returns the value of the last expression in the function body.
Go ahead try it out in the repl.
user=> (do-math 4 5)
20
In do-math
(* a b)
was the last expression.
Try switching around the expressions in do-math
’s body and see how the output changes.
Function names can have special characters too
Did you notice that there was a -
in do-math
?
This would be illegal in other languages.
you can’t define a python function named do-math
, instead it would be do_math
.
In fact, ?
, >
, !
, etc are perfectly valid to use in clojure function names.
Just a few examples of clojure functions that use these.every?
, conj!
.
Don’t worry if you don’t understand what these do, just know that these are valid clojure names.
Recap
- You learnt the function call syntax
(function-name arg1 arg2 arg3)
. - You learnt how to define new functions with
defn
. - You learnt that function names can have special characters like
?
and-
.
Exercises
I’m giving you some simple exercises, please please do them. If you want me to look at your solutions or help you out, or YOU HAVE FEEDBACK, tweet at me https://twitter.com/the_lazy_folder (especially if you have feedback).
Problems
create a function
subtract
that subtracts two numbers.user=> (- 4 2) 2
create a function
add-three-nums
that adds three numbers.user=> (add-three-nums 1 2 3) 6
BONUS create a function
I-want-the-one
that takes no arguments and returns1
always.user=> (I-want-the-one) 1 user=> (I-want-the-one) 1 user=> (I-want-the-one) 1
why can’t I do this ("hello")
?
if you tried to get the repl to print “hello” you probably got this error.
user=> ("hello")
Execution error (ClassCastException) at user/eval2017 (REPL:1).
class java.lang.String cannot be cast to class clojure.lang.IFn (java.lang.String is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'app')
this is because… *screeching brakes* I think that’s enough for today. I’ll explain this in part 2 of the series.
For now, use this.
user=> "hello"
"hello"