Clojure
Clojure Notes
Some of useful links
Solving Problems Declaratively - Mark Engelberg
Clojure course
Source: Clojure: The Complete Beginner’s Guide
Setup Dev Environment
Tools needed:
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