Clojure basics

download Clojure basics

If you can't read please download the document

Transcript of Clojure basics

Introduction to Clojure

Sidharth Khattri

Knoldus Software LLP

Clojure is a Functional Lisp (List Processing) which runs on JVM.

It extends the principle of Code-as-Data system to include Maps and Vectors. Everything in clojure is written inside a data structure referred to as the S-expressions, i.e nested lists.

Eg: (/ 4 (+ 1 2)) => ?

Function Name

Arguments

Every operation in clojure is done using a Post-Fix notation

Experimenting with clojure is quite easy. In order to get started with clojure you need to follow the instructions on http://leiningen.org/ to set up clojure environment on your system. Leningen is used for project automation.

Most popular IDE used for clojure is LightTable which can be download from http://www.lighttable.com/

You can fire up clojure's repl on linux terminal using:lein repl or you can directly use a Live REPL in LightTable.

You can also use clojure in Eclipse using CounterClockwise plugin.

Everything that you need to know about clojure can be found in the clojure cheatsheet at the following url: http://clojure.org/cheatsheet

Even though a lot of parentheses can confuse programmers at first, LightTable(IDE) can make programming in clojure really easy. A sample of what usage of parentheses I'm talking about:(filter #(if(zero? (rem % 3)) true) (map #(+ % 1) (range 10)))=> ?

The above code in LightTable should look something like this:

Last line of a function can return another function, i.e a Higher Order Function as illustrated in the following example:(defn attribute [who?] (if (= who? "superman") #(str "Superman " %) (fn[x] (str "Human " x))))((attribute "superman") "Flying") => "Superman Flying"

(if 0 Yee! True Huh! False) => ?

(if 1 Yee! True Huh! False) => ?

Concept of truthy and falsy

(if 0 Yee! True Huh! False) => Yee! True

(if 1 Yee! True Huh! False) => Yee! True

Concept of truthy and falsy

Concept of truthy and falsy

Everything in clojure is true except false or nil So, (if nil Yee! True Huh! False) => Huh! False

Data Structures

Clojure supports a number of data structures:Lists, Vectors, Maps, Sets

All clojure data structures are persistent data structures. Internally they're implemented as a tree.

Simplest way to define these data structures:'(1 2 3) defines a list[1 2 3] defines a vector#{1 2 3} defines a set{:1 one :2 two} defines a map

Nesting

Searching and updating nested structures is very easy.

Searching:(def n {:india {:newdelhi {:knoldus {:address "30/29, 1st Floor, East Patel Nagar"}}} :usa {:california {:knoldus {:address "743, Catamaran Street "}}}})user=> (get-in n [:india :newdelhi])Returns {:knoldus {:address "30/29, 1st Floor, East Patel Nagar"}}

Updating: (assoc-in n [:india :newdelhi :knoldus :number] 911142316525)Returns {:india {:newdelhi {:knoldus {:number 911142316525, :address "30/29, 1st Floor, East Patel Nagar"}}}, :usa {:california {:knoldus {:address "743, Catamaran Street "}}}}

Remember that the value of n hasn't changed in any case.

Threading Operators

The previous code that we used: (filter #(if(zero? (rem % 3)) true) (map #(+ % 1) (range 10))) Is same as: (->> (range 10) (map #(+ % 1)) (filter #(if (zero? (rem % 3)) true))) The threaded version is much cleaner

Threading Operators

In Nested structures example that we used: (def n {:india {:newdelhi {:knoldus {:address "30/29, 1st Floor, East Patel Nagar"}}} :usa {:california {:knoldus {:address "743, Catamaran Street "}}}}) We can use: (-> n :india :newdelhi :knoldus :address) Instead of: (:address (:knoldus (:newdelhi (:india n))))

Loops

For loop:(for [x (range 1 10) :when (even? x)] x)=> (2 4 6 8)

While loop:(while 0 (println hello))

Loops with side effects:(dotimes [x 5] (print x)) => 01234nil(doseq [x [3 2 1]] (print x)) => 321nil

Binding Form - let

We use the let form to bind data structures to symbols.

Example:(let [x 10 y 11 m (* x y)] m)user=> m

Binding Form - let

We can also use let binding for destructuring:

(defn index-sum [v & i] (let [[x :as ind] (map #(get v %) i)] (reduce + ind)))(index-sum [1 2 3 4 5 6 7 8 9] 1 3 5) => ?

Built-in Parallelism

map function will take more time as compared to the pmap function:(time (doall (map (fn[x] (Thread/sleep 3000) (+ x 5)) (range 1 5))))=> "Elapsed time: 12000.99432 msecs" (6 7 8 9)(time (doall (pmap (fn[x] (Thread/sleep 3000) (+ x 5)) (range 1 5))))=> "Elapsed time: 3002.989534 msecs" (6 7 8 9)

Futures

Futures can be used to send any calculation intensive work in the background while continuing with some other work.

Defining futures:(def f (future some-calculation-intensive-work))

Example:(defn show-result[] ;;do things (def f (future some-calculation-intensive-work)) ;;prepare gui to display result @f);;wait until the result is returned

Atoms, refs and agents

Atoms, refs and agents are the three options available for maintaining non-local mutable state in clojure

Atoms are for Uncoordinated Synchronous access to a single Identity.

Refs are for Coordinated Synchronous access to Many Identities.

Agents are for Uncoordinated Asynchronous access to a single Identity.

Atoms

Defining an atom:(def a (atom {:a 1}))

Getting the value stored in an atom:(deref a) or @a

Changing the value of an atom:(swap! a #(assoc % :b 2)) => {:a 1 :b 2}

or(reset! a 0) => Exception or changed value?

Refs

Defining refs:(def tasks-to-be-done (ref #{2 9 4}))(def tasks-done (ref #{1 3 5}))

Coordinated change:(dosync (commute tasks-to-be-done disj 2) (commute tasks-done conj 2))

Accessing values of refs:@tasks-to-be-done => #{4 9}@tasks-to-be-done => #{1 2 3 5}

Agents

Can be useful in fork/join solutions.

Defining an agent: (def a (agent 0))

Dispatching actions to an agent:(dotimes [x 3] (send-off a (fn[x] (Thread/sleep 3000) (inc x))))@a => ?

In case we want to wait until the above code snippet has finished processing, we can use:(await a)

Arrays

Defining an array:(def a1 (make-array Integer/TYPE 3))(pprint a1) => [0, 0, 0]

(def a2 (make-array Integer/TYPE 2 3))(pprint a2) => [[0, 0, 0], [0, 0, 0]]

(def a3 (to-array [1 2 3 4 5]))(pprint a3) => [1, 2, 3, 4, 5]

Arrays

Manipulating arrays:(def a1 (make-array Integer/TYPE 3))(aset a1 1 10))(pprint a1) => [0, 10, 0]

(def a2 (make-array Integer/TYPE 2 3))(aset (aget a2 0) 1 10)(pprint a2) => [[0, 10, 0], [0, 0, 0]]

Datatypes

defrecord creates an immutable persistent map (class-type datatype)(defrecord Hobbit [fname lname address])(defrecord Address [street town city])(def bb (Hobbit. "Bilbo" "Baggins" (Address. "Bagshot row" "Hobbiton" "Shire")))

user=> bb#user.Hobbit{:fname "Bilbo", :lname "Baggins", :address #user.Address{:street "Bagshot row", :town "Hobbiton", :city "Shire"}}

(-> bb :address :city)Shire

Datatypes

deftype creates a bare-bones object (class-type datatype). Preferred for java inter operability.(deftype Hobbit [fname lname address])(deftype Address [street town city])(def bb (Hobbit. "Bilbo" "Baggins" (Address. "Bagshot row" "Hobbiton" "Shire")))

user=> bb#

(.street (.address bb))"Bagshot row"

Protocols

Dataype are used to implement protocols or interfaces.(defprotocol Dialogue (deliver-dialogue [d]))

(defrecord Where? [place] Dialogue (deliver-dialogue [d] (str "One does not simply walk into " place)))

(def LOR (Where?. "Mordor"))(deliver-dialogue LOR)=> "One does not simply walk into Mordor"

Thank You :)