Hi! Nice to see that you want to learn more about ClojureScript. Firstly, you should know how to write p5 sketches before doing this quick tutorial. Otherwise, you should checkout p5's own getting started tutorial.
This is a super quick tutorial to get you started writing p5 sketches in ClojureScript and learn more about the Clojure/ClojureScript language.
ClojureScript is a Clojure library that compiles ClojureScript into JavaScript. And Clojure is a dialect of Lisp, a family of languages characterized by the use of S-expressions (lots of parentheses). Let's take a look at what this looks like.
(+ 1 1)
(+ 2 4 6 8 10)
(/ (* 100 0.5) 100)
1 + 1
2 + 4 + 6 + 8 + 10
100 * 0.5 / 100
The next thing you might want to do is define your own functions to do some
work. You would use the defn
macro for this.
(defn add [a b]
(+ a b))
(add 100 10) ;returns 110
It's important to note that the last expression that gets evaluated in the function, is the value that gets returned from the function.
In JavaScript, it's common to define a bunch of global variables at the beginning of the sketch for use later.
const PI = 3.1415
const TAU = PI * 2.0
const NUM_ROWS = 999999
const NUM_COLS = 999999
To do this in Clojure, you would use def
.
(def PI 3.1415)
(def TAU (* PI 2.0))
(def NUM_ROWS 999999)
(def NUM_COLS 999999)
Like the JavaScript equivalent, these are constants and are immutable! You cannot change the values of these constants.
Sometimes you need a variable that gets mutated for keeping track of some kind of value.
let count = 0
lotsOfItems.forEach(item=>{
doSomething(item)
count++
})
This is where atoms come in. They are a special kind of variable that is
mutable. Take a look at this example, which also uses a for
and let
.
(let [count (atom 0)]
(for [item lotsOfItems]
(doSomething item)
(swap! count inc)))
The let
block makes the variable count
, which is an atom with a value of 0,
available for the rest of the expression. Within this block, the for
expression
iterates over lotsOfItems
. We're using it like a for-loop in thise case. Finally,
at the end, we inc
(increment) the value of count using swap!
. swap!
takes as its arguments an atom and a function. It applies the function to the
value of the atom and replaces it with the result.
I know this sounds a bit strange. Why are we making a mutable variable so hard to mutate? It's to ensure that when you are actually mutating something, you do it deliberately and not by accident.
Now we come to the fun part! How do we call JavaScript functions from
ClojureScript? If you've seen the default sketch here, then you'll notice that
every p5 function call is prefixed with js/
.
(defn draw []
(js/background 0)
(js/fill 255 0 0)
(js/noStroke)
(js/circle 200 200 200))
Everything you would normally call as a function from p5 will work like this. Of course, there are more ways to work with JavaScript, including objects and classes.
console.log(new Date())
(js/console.log (js/Date.)) ; class is postfixed with a period
For more information about interoperability with JavaScript, I recommend reading this article.
That's it for this quick primer on ClojureScript! You should now know just enough to begin creating some sketches in ClojureScript. Obviously, you won't be creating the next Tyler Hobbs masterpiece. But hopefully, this little bit of information will get you going and you'll find out more about Clojure as you create.
If you'd like to deepen your understanding of Clojure, I recommend looking into the following topics:
- control flow
- map & apply
- hashmaps
- higher order functions
->
(thread) macro