Demystifying Clojure
It’s been over a month since I started learning Clojure and if anyone tells you it’s a mind shift believe me it is! My purpose for this post is to help you make that shift and develop the mindset that can help you learn Clojure(or at least what is helping me).
Two weeks ago I started on a book Clojure for the brave and true by Daniel Higginbotham which was recommended to me by my mentor/supervisor. What I really love about the book is the comparison between ruby and Clojure syntax. I have a background in ruby, therefore, this came in handy in helping me familiarize with Clojure syntax.
A culture that I am adapting while going through the book is spending a maximum of one hour every day to learn. Not just read but internalize and practice. I then get to explain to my supervisor what I have learnt the previous day. It’s easy to learn and just keep it to yourself but you grasp even more when you can explain to someone else what you have understood. I also get clarity on concepts I never understood the day before and an opportunity to be corrected. Believe me, the language programmers use (assign, pass, call) is not a piece of cake! That too, I believe takes practice.
This takes me to my first topic, assigning variables.
We assign variables in almost all languages but in Clojure, we bind names to a value. Confused? Don’t worry, if it makes you feel better it took me about a month to understand just that!
Let’s take an example in ruby
You can make several assignments to one variable in ruby(technically, in other languages too).
Here is a Clojure example:
user=> (def first-name "awesome thang")
=> first-name
;;returns "awesome thang"
Thought you should know: name
is a keyword, therefore, you can’t use it as a ‘variable’ /name hence the first-name
We could assign the ‘variable’ first-name
to other values in Clojure like so
but it will be super confusing and you may give up Clojure before you get to the best part!
So, we can proceed to….
DATA STRUCTURES!
According to Wikipedia, a data structure is a collection of data values, the relationships among them, and the functions or operations that can be applied to the data.
It’s how data is stored and organize so that it can be used by operations efficiently.
In Clojure, data structures are immutable. Meaning you can’t change the value
In the example above, we bind a vector to my-flowers
which will return the vector when called. We then add an element roses
to the vector my-flowers
using conj (we will learn about this function ahead). It will return a new vector containing our added element when called. Call my-flowers
again and you will realize our initial vector is returned. That is immutability. You can read it’s importance here
With this in mind, we can move forward with data structures.
1. Numbers
Consists of integer,floats,ratios e.t.c
user=>(type 100)
java.lang.Longuser=> (type 1.2)
java.lang.Doubleuser=> (type 1/3)
clojure.lang.Ratio
Thought you should know: type returns the metadata of the value
2. String
String represents text.
user=> (str "I love tic-tacs!" " and sodas")
;;I love tic-tacs!and sodas
Thought you should know: str function is used to concatenate and we use double quotes for strings declaration.
3. Maps
denoted as {}
example of a string map
(def string-map {"a" 1 "b" 2 "c" 3})
example of a keymap(contains keyword and values)
(def key-map {:a 1 :b 2 :c 3})
We can use hash-map
to create maps
user=> (hash-map :a "jane" :b "doe")
{:b "doe", :a "jane"}
and sorted-map
to create a sorted map according to keys
(sorted-map :b "jane" :a "doe")
{:a "doe", :b "jane"}
We use get
and get-in
to look up values in a map and a nested map respectively.
(get {:name "jane" :age "34"} :age)
;; "34"(get-in {:ladies {:name "jane"}} [:ladies :name])
;; "jane"
Thought you should know: The keywords in a map have to be unique. You cannot repeat the same keyword example: using :name
in the same map will return an error.
We can also use keywords to look up values like so:
(:age {:name "jane" :age "34"})
;; "34"
4. Vectors
Vectors are a collection of values which are 0-indexed
denoted as []
example [1 2 3]
we look up values in vectors using the index. Therefore it returns the element at the indexed position. For example
(get[1 2 3 4 5] 1)
;; 2
we return 2 because it’s the 1st index in the vector.
Thought you should know: Vectors can be of any type and you can mix them up.Example:
(get [1 "lala lila" true] 2)
;; true
vectors are created using vector
(vector 1 2 3 4)
;; [1 2 3 4]
conj
and cons
are functions used to add elements to a vector
notice the difference? conj
adds an element at the end of the vector and cons
add it at the beginning. Cool huh?!
5. List
List is almost similar to vectors except you can’t use get
to get elements.
The basic syntax of a list is:
`(1 2 3 4 5)
(1 2 3 4 5)
We use nth to retrieve elements in a collection.
user=> (nth `(1 2 3 4 5) 2)
3
We can create lists using list
user=> (list 254 255 256)
(254 255 256)
List elements can also be of any type just like in vector. We also add elements using conj
and cons
in lists. They both add elements at the beginning of the list.
user=> (cons 4 '(1 2 3))
(4 1 2 3)user=> (conj '(1 2 3) 4)
(4 1 2 3)
6. Sets
I never knew how important sets were till I started implementing them on my code and challenges. Set in Clojure is a collection of unique values.
There are two types of sets: hash-set
and sorted-set
user=> (hash-set 1 2 3)
#{1 3 2}user=> (sorted-set 1 2 3)
#{1 2 3}
As defined what sets are, we can have multiple similar elements but only one unique element will be returned. Example:
user=> (sorted-set 1 1 1 2 2 3 3)
#{1 2 3}
We use conj
to add an item to a set
user=> (conj #{1 2 3} 4)
#{1 4 3 2}
When you add an element which already exists in a set, the set will contain only one of that value.
user=> (conj #{1 2 3} 3)
#{1 3 2}
We use set
to create a set from vectors and lists
user=> (set `(1 2 3))
#{1 3 2}user=> (set [1 2 3])
#{1 3 2}
To check values in a set, we use contains?
, get
and using a keyword as a function.
;; using contain returns a true or false value.user=> (contains? #{1 2 3 4 5} 4)
true
user=> (contains? #{"cat" "dog" "parrot" "fish"} "cat")
true;; using get
user=> (get #{"cat" "dog" "parrot" "fish"} "fish")
"fish";; using the keyword
user=> (:fname #{:fname :lname :sname})
:fname
And that is it!
I also found these articles to be very helpful in understanding Clojure and I frequently refer to them:
https://learnxinyminutes.com/docs/clojure
Hope you enjoyed this and happy coding!😉🙂