Nuggets of Clojure Attempt

Attempt of Nuggets of Clojure.

I do not think this is the best way to learn about Lisp, but it's a cool way at least :)

I'm going to explain Clojure using itself.


In Clojure, we have data

42

42 is a number. What does 42 do? Nothing.

We have more data

()

This is a list, with nothing in it

+

This is a symbol

(+ 42 42)

This is a list with 3 elements: +, and 2 42s.

It happens there's a function called + in Clojure that adds numbers together.

Unfortunately, our list is just a list. Like a number, it doesn't do anything.

Fortunately, there's another function in Clojure called eval which lets us call these lists.

Unfortunately, just like with +, we don't have any way to call it.

How do you call functions in Clojure? By passing code to eval.

(eval 42)

This, however, is still a list, and lists don't do anything.

Because we only have lists, let's have someone else call our code for us.

$ clj
Clojure 1.13.0-alpha6
user=>

Now we have a prompt to write code into

user=> 42
42
user=> (+ 42 42)
84

The first line, with user=>, takes code and passes it into eval for us. When passed a number, eval returns it. When passed a list, eval calls it.

Now we can call code, like + from earlier!

There are other ways to get eval called on your code. You could stick it in a file and run it, but we don't need to do that. We'll stick with the REPL that clj started.

What is a REPL? "REPL" is an acronym of 4 parts. Right now, the only part that matters is "E", which stands for eval. The REPL evaluates things, like lists and numbers.

I cheated though and used the + function, which I promised exists, but never defined.

Let's rectify that

(def + clojure.lang.Numbers/add)

This "defines" + to be the function clojure.lang.Numbers/add.

add is a static method of the Java class clojure.lang.Numbers.

Clojure is a "hosted" language, which means it can call code from its host, which is the JVM in this case.

Why does that matter? Because Java already knows how to add numbers together. It has for decades, and now we can just punt. Great!

Now about def? I used it without defining it first, so what is the definition for def?

Well, it doesn't have one. The only way to define things is with def, and so def would have to exist in order for it to be able to define itself, which is impossible.

So how does def work? Somehow, eval knows about it, and knows how to evaluate it, and that's good enough for us.

These are called special forms, and there are a few of them. They are functions that could only be defined using themselves. Another example is if: the conditional. You can only make a conditional using a conditional. You can't go down branching paths without the ability to go down branching paths, and therefore, if cannot be defined without if.

These can be thought of as the base of Clojure, upon which the entire rest of the language is defined.

How many special forms are there? Let's pretend there are 6: def, if, fn (which creates lambdas/functions), let (which defines locals, unlike def which defines globals), try (which does try/catch/finally error handling), and quote (which tells eval not to evaluate its argument, and leave it as it is)

The rest are niche, not really fundamental to the language1, and we won't need them.



  1. Okay not really, what I mean is that they are either niche (like monitor-enter and monitor-exit, case, deftype, etc.); could have been defined inside of Clojure itself if the language were more concerned with purity than practicality, which it is not; or are kind of legacy features like . and new that could be implemented another way today, and I'm just going to pretend Clojure works this way because it's nicer and has the same effect.

    ↩