Clojure Notes

Solving Problems Declaratively - Mark Engelberg

Mark Engelberg - Github repos

Clojure course

Source: Clojure: The Complete Beginner’s Guide

Setup Dev Environment

Tools needed:

  1. Java
  2. Java SDK
  3. Leiningmen
  4. IntelliJ Idea
  5. Cursive
  6. Calva

Using Leiningen

Create new app

lein new app app-name

Run app

lein run

Using REPL

lein repl

Basics

  • Clojure is complied language
  • It complies into JVM i.e into .class files
  • Clojure everything is a list
    • ex: (verb param1 param2…)
  • Everything has a return value

Functions

Function Example

(ns example.functions
    (:gen-class))

(defn -main
    "Example function"
    []
    (println "This first function")
    (+ 2, 3))

Anonymous Function

Syntax1: Starts with # and takes arguments has %

Example

(#(println "This is my" % "anonymous" %) "first" "function")
  • defn creates a function
  • def assigns name to the function

Syntax2:

  • Starts with fn as below
(fn [x y] ; Takes two arguments
(println "Making a set")
#{x y}) ; returns set
  • Calling the above anonymous function
((fn [x y]
(println "Making a set")
#{x y})
1 2) ; calling anonymours function with arguments 1, 2

Example

(def increment (fn [x] (+ x 1 )))

(defn increment_set
    [x]
    (map increment x))

Data Types

  • Clojure inherits the data types from Java
  • Some of the data types supported are:
    • Integer
    • Float
    • Hex
    • Null
    • Boolean
    • String
    • Keyword

Example:

(defn datatypes
    []
    (def a 1)
    (def b 1.5)
    (def c 0xabc12)
    (def d nil)
    (def e true)
    (def f "Hello World")
    (def g 'important)

    (println a)
    (println b)
    (println c)
    (println d)
    (println e)
    (println f)
    (println g))

Other datatypes

  • Integer
    • Short
    • Long
    • Octal
    • Hexadecimal
  • Atoms
  • Java data types
    • Java.Lang.Byte
    • Java.Lang.Short
    • Java.Lang.Integer
    • Java.Lang.Long
    • Java.Lang.Float
    • Java.Lang.Double

Variables

  • Variables are immutable, means we can’t change the value in them
  • Variables can’t start with number. They can have numbers, letters and underscore
  • Variables are case sensitive

Operators

Arithmetic Operators

(+ 1 2)
(- 1 2)
(* 1 2)
(/ 1 2)
(INC 5)
(DEC 5)
(MAX 10 5)
(MIN 10 5)
(REM 10 5)

Relational Operators

(= 2 2)
(NOT= 2 4)
(< 2 4)
(<= 2 4)
(> 2 4)
(>= 2 4)

Logical Operators

(AND True False)
(OR True False)
(NOT False)

Bitwise Operators

(BIT-AND 0110 1100)
(BIT-OR 0110 1100)
(BIT-XOR 0110 1100)
(BIT-NOT 1100)

Operator Precedence

  • All the expression are prefix notation and in parenthesis, so there is no need for operator precedence

Composite Data Types

Set

  • Set of different data types
  • Immutable
  • Efficient

Example:

#{}
#{1 2 3.4 "Dog" 'Help}

Map

  • Key value pairs
  • Immutable
  • Efficient

Example:

{:Key1 "Value"}
{1 42, 2 1.5, "Pet" 'Dog}

Vector

  • Array
  • Immutable
  • Efficient
  • Indexed by Position

Example:

[]
[1 2 3 4 5]
[1 2 "Hello" "World"]

List

  • Make up the code
  • Immutable
  • Efficient

Example:

(1 2 3)
(1 "Two" 'Three (1 2 3 4))
(defn foo [] (println "Hello World"))
(foo)

Conditional Statements

If condition

Example

  • Single statement under the if
(if (= 5 5)
    println "Condition is true"
    println "Condition is false"
)
  • Multiple statements under the if
(if (and (= 5 5) (= 7 7) (or( (= 8 8) (not true) )))
    (do (println "Condition is true"))
    (do (println "Condition is false"))
)

Case condition

Example

(case "dog"
 "dog" (println "its dog")
 "horse" (println "its horse")
 "tiger" (println "its tiger")
 )

Cond

Example

(def amount 99)
(cond amount
    (> amount 100) (println "amount > 100")
    (< amount 100) (println "amount < 100")
    (= amount 100) (println "amount = 100")
    :else (println "from else")
)

Loops

loop

Example

(loop [x 0]
    (when (< x 10>)
        (println x)
        (recur (inc x))))

dotimes

Example

(dotimes [x 10]
    (println x)))

while

Example

(def x (atom 0))
  (while (< @x 10)
    (do
      (println @x)
      (swap! x inc)))
      ```

doseq

Example

(defn seqloop
  "seq loop"
  [seq]
  (doseq [x seq]
    (println x)))

(seqloop [2 3 4 5])

Atoms

  • Thread safe way of changing the value in clojure

Example

(def x (atom 0)) (swap! x inc)))

Sequence

  • cons: Adds item to the begining of the sequence
  • conj: Similar to the cons but add the item based on the context of the arguments
  • concat: Joins to sequences
  • distinct: Removes duplicates
  • sort: Sorts the items in the sequence
  • first: Gets the first item in the sequence
  • rest: Excludes the first and gets the rest of the items
  • last: Gets the last tiem in the sequence

Example

(defn seqfn
  "Sequence function"
  []
  (def colors (seq ["green" "red" "blue"]))
  (println (cons "yellow" colors))
  (println (cons colors "yellow"))
  (println (conj colors "purple"))
  (println (concat colors (seq ["white" "Black" "Grey"])))
  (println (distinct (seq [1 2 8 2 3 2 4 5])))
  (println (sort (seq [1 2 8 2 3 2 4 5])))
  (println (first colors))
  (println (rest colors))
  (println (last colors))

Output:

(yellow green red blue)
((green red blue) y e l l o w)
(purple green red blue)
(green red blue white Black Grey)
(1 2 8 3 4 5)
(1 2 2 2 3 4 5 8)
green
(red blue)
blue
(green red blue)
nil

Struct

  • struct-map allows you to specify the property name
  • assoc allows to change the value for the struct and define a new struct
  • using assoc we can also add new keys

Example:

(defn structfn
  "Structure function"
  []
  (defstruct pet :PetType :PetName)
  (def mypet (struct pet "Dog" "Tommy"))
  (println mypet)
  (def mysecondpet (struct-map pet :PetName "Tuffy" :PetType "Dog"))
  (println mysecondpet)
  (def myNewPet (assoc mypet :PetName "Raju"))
  (println myNewPet)
  (def myNewOtherPet (assoc mypet :PetAge "10"))
  (println myNewOtherPet))

Destruct

Example:

(defn destruct
  "Destruct"
  []
  (def myVect [1 2 4 "xyz" "Test"])
  (let [[a b c d] myVect] (println a b c d))
  (let [[a b c & rest] myVect] (println a b c rest))
  (def myMap {'state "Telangana" 'city "Hyderabad"})
  (let [{s 'state c 'city} myMap] (println s c))
  (let [{s 'state c 'city x 'capital} myMap] (println s c x)))

Output:

1 2 4 xyz
1 2 4 (xyz Test)
Telangana Hyderabad
Telangana Hyderabad nil
nil

Exception Handling

Example:

(defn exceptionHandler
  "Exception handler"
  [x]
  (try
    (inc x)
    (catch Exception e (println "expception:" (.getMessage e)))
    (finally (println x))))

Namespace

Example:

(ns example.petage
  (:require [clojure.string :refer [capitalize]]))

(defn transformString
  ""
  []
  (println (capitalize "hello")))

Watch

Example:

(defn watchfn
  "Watch function"
  []
  (def x (atom 5))
  (add-watch x :watchatom
             (fn [key-value watch-value old-state new-state]
               (println "Key:" key-value)
               (println "Watching:" watch-value)
               (println "Old-State:" old-state)
               (println "New-State:" new-state)))
  (reset! x 50)
  (remove-watch x :watchatom)
  (reset! x 100))

Output:

Key: :watchatom
Watching: #atom[50 0xfb805b5]
Old-State: 5
New-State: 50
100

Agents

  • Agents are similar to atoms but agents are asynchronous
  • await-for awaits until the amount is incremented or timed out
  • agent-error retrieves any error generated on agents

Example:

(defn agentfn
  "Agent function"
  []
  (def amount (agent 100))
  (println @amount)
  (send amount inc)
  (await-for 1000 amount)
  (println @amount)
  (println (agent-error amount)))

Output:

100
101
nil
nil

Reference Values

  • Reference values are simiplar to atoms and agents. Only difference is that the reference value changes are done in transactions

Example:

(defn reffn
  "Reference function"
  []
  (def amount (ref 200))
  (println @amount)
  (dosync
   (ref-set amount 300))
  (println @amount)
  (dosync
   (alter amount inc))
  (println @amount))

Output:

200
300
301
nil