Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

(cons 'introduction (cons 'to (cons 'clojure '())))




November 25th, 2013


Ivan Willig and Peter Lubell-Doughtie

Intent Media

Outline

Why this talk?

What is Clojure?

Rich Hickey

What is Clojure?

(take 4 (range 10000))

What makes Clojure a Lisp?

Calling a function in JavaScript

funName(arg1, arg2, arg3);


Calling a function in Clojure

(fun-name arg1 arg2 arg3)

See Little Schemer

How Clojure compares to JavaScript

Defining variables


Defining variables in JavaScript

var now = new Date();


Defining variables in Clojure

(def now (Date.))

Calling instance methods


Calling instance methods in JavaScript or Java

now.getDay();


Calling JavaScript or Java instance methods in Clojure

(.getDay now) ;; call a method without arguments

Calling static methods


Calling a static method in JavaScript or Java

Date.parse("March 21, 2012");


Calling a static JavaScript or Java method in Clojure

(Date/parse "March 21, 2012") ;; 1332302400000

Defining functions


Defining a function in JavaScript

var add10 = function (x) { return x + 10; };

function add10(x) { return x + 10; };


Defining a function in Clojure

(fn [x] (- 20 x)

(def add10 (fn [x] (+ 10 x)))

(defn add10 [x] (+ 10 x))

(add10 -4) ;; 6

Defining functions

Taking variable arguments in a function

var reduce_PLUS = function () {
    var args = Array.prototype.slice.call(arguments);
    return plus.apply(this, args);
};

reduce_PLUS(1, 2, 3); // 6


(defn reduce+ [& args] (apply + args))

(reduce+ 1 2 3) ;; 6

Defining functions

Function with different arity

(defn argcount
  ([] 0)
  ([x] 1)
  ([x y] 2)
  ([x y & more] (+ (argcount x y) (count more))))

Defining functions

From util.math

(defn round [x precision]
  (MathUtils/round x precision))

(round 5.5 0) ;; 6.0


From util.time

(defn get-requested-at [typed-data]
  (SystemTimeFactory/getMillis
   (.getRequestedAt typed-data)))

Defining functions

From util.cascalog

(defn within-window?
  "True if t2 between t1 and a window amount of time
 after t1."
  [t1 window t2]
  (and
   (< t1 t2)
   (< t2 (+ t1 window))))

(within-window? 3 1 3.9) ;; true
(within-window? 3 1 4)   ;; false

Namespaces

a.clj

(ns a
  (:import [java Math]))

(def var "10")
(defn parse-int [x]
    (when-not (integer? x)
        (Integer/parseInt x)))

b.clj

(ns b
 (:use     [a])
 (:use     [a :only (parse-int)])
 (:require [a :as utils]))

(utils/parse-int "10")
(utils/parse-int 10)

Clojure basic collections

(def a [1 "string" :keyword {:key "value"}])

(def a {:a 1 :b "number"})

Seq functions

(conj [] :a)         ;; [:a]

(conj {:a 1} [:b 2]) ;; {:b 2, :a 1}

(conj #{:a} :b)      ;; #{:a :b}

Lazy data structures

Add doall to evaluate an expression

From extract.signals-test

(doall (map
        signals-tests-for-site
        [SiteType/SITE1
         SiteType/SITE2
         SiteType/SITE3
         SiteType/SITE4]))

Data structures are persistent

(def a [1 2 3])
(def b (conj 0 a))
(= a [1 2 3]) ;; true
(= b [0 1 2 3]) ;; true

Data structures are persistent


shared memory

vector

(def a [1 2 3])       ;; create
(cons 1 [])           ;; '(1)
(cons 1 [2 3])        ;; '(1 2 3)
(count a)             ;; 3
(first a)             ;; 1
(second a)            ;; 2
(rest  a)             ;; [2 3]
(contains? [1 2 3] 0) ;; true
(contains? [1 2 3] 3) ;; false
(a 0)                 ;; 1
(get a 1)             ;; 2
(get a 10 :default)   ;; :default
(nth a 1)             ;; 2
(nth a 10)            ;; throw IndexOutOfBoundsException

hash-map

A collection of key value pairs

(def ivan {:name "Ivan Willig"
           :age 28
           :address "360 Adams Street"})

(count ivan)            ;; 3
(keys ivan)             ;; (:age :name :address)
(vals ivan)             ;; (28 "Ivan Willig" "360 Adams Street")

(:name ivan)            ;; "Ivan Willig"
(:age  ivan)            ;; 28

(contains? ivan :name)  ;; true

hash-map

(get ivan :name)            ;; "Ivan Willig"
(get ivan :sane? "No !!!")  ;; "No !!!"

(assoc ivan :mood "good")
;; {:age 28, :name "Ivan Willig", :mood "Good", 
;;  :address "360 Adams Street"}

(conj ivan ["mood" "good"])
;; {:age 28, :name "Ivan Willig", "mood" "good",
;;  :address "360 Adams Street"}

(println ivan)
;; {:age 28, :name "Ivan Willig", :address "360 Adams Street"}

set

A collection of distinct elements

(require '[clojure.set :as s])

(def a   #{1 2 3 4 5})

(def b   #{2 3 4})

(def c   #{6 7 8 9 10})

(s/subset? b a) ;; true

(s/union a c) ;; #{1 2 3 4 5 6 7 8 9 10}

(s/select integer? #{1 2 3 :a :b :c}) ;; #{1 2 3}

map, reduce, filter

(map inc [1 2 3 4 5])
;; (2 3 4 5 6)
(reduce + [1 2 3 4 5])
;; 15
(reduce + 10 [1 2 3 4 5])
;; 25)
(filter (fn [x] (< x 10)) [1 2 3 10 23 45])
;; (1 2 3)

Iteration

(doseq [i [1 2 3]] (println i))
;; 1
;; 2
;; 3
;; 4
;; nil

(for [i [1 2 3 4 5]] (* 3 i))
;; (3 6 9 12 15)
(doseq [[key value] {:a 1 :b 2 :c 3 :d 4}]
   (println key)
   (println "---")
   (println value))
;; :a
;; ---
;; 1

Collections are functions

([1 2 3 4]  0)  ;; 1

(#{1 2 3 4} 0)  ;; nil

(#{1 2 3 4} 1)  ;; 1  ??? why

({:name "thing"} :name) ;; "thing"

Defining new types


Define a new type in JavaScript

var Point = function (x, y) {
    this.x = x;
    this.y = y;
};


Define a new type in Clojure

(defrecord Point [x y])

(def p (Point. 10 5))

(print (:x p)) ;; 10
(print (:y p)) ;; 5

Defining new types

defrecord

(defrecord Person [name age address])
(def dude (Person. "John Smith" 35 "1234 Ave E"))
(:name dude)     ;; "John Smith"
(= dude (Person. "John Smith" 35 "1234 Ave E")) ;; true


defrecord implements the following Clojure and Java interfaces

clojure.lang.IObj
clojure.lang.ILookup
clojure.lang.IKeywordLookup
clojure.lang.IPersistentMap
java.util.Map
java.io.Serializable

Defining new types

deftype

(deftype Person [name age address])

(def dude (Person. "John Smith" 35 "1234 Ave E"))

(.name dude) ;; "John Smith"

Extending existing types


Extending existing types in JavaScript

Point.prototype.distance = function (other) {
    var dx = this.x - other.x,
        dy = this.y - other.y;
    return Math.sqrt(dx * dx + dy * dy);
};

Extending existing types in Clojure

(defprotocol IDistance
    (distance [self other]))

(extend-type Point
  IDistance
    (distance [self other]
      (let [dx (- (:x self) (:x other))
            dy (- (:y self) (:y other))]
        (Math/sqrt (+ (* dx dx)
                      (* dy dy))))))

Extending existing types in Clojure

(def p0 (Point. 3 10))

(def p1 (Point. 6 6))

(distance p0 p1) ;; 5.0

(distance {:x 10 :y 10} p0) ;; throws 
                            ;; IllegalArgumentException
(distance p0 {:x 10 :y 10}) ;; 5.0

Clojure is homoiconic

When we suspend evaluation we can work with the raw source code as a Lisp tree. All code can be thought of as a tree.


(* (+ 2 (* 4 6))
   (+ 3 5 7))


s-expr

Macros

Macros are different from functions

(defn unless' [expr then] (if (not expr) then nil))

(unless' false (println "should not run"))
;; should not run

(defmacro unless [expr then] `(if (not ~expr) ~then nil))

(unless false (println "should not run"))
;; nil

(defn unless2 [expr form] (if (not expr) nil (form)))

(unless2 false (fn [] (println "should not run")))

Macros

Examples from clojure.core

(defmacro if-not
  ([test then] `(if-not ~test ~then nil))
  ([test then else]
  `(if (not ~test) ~then ~else)))
(defmacro when
  [test & body]
  (list 'if test (cons 'do body)))
(defmacro when-not
  [test & body]
  (list 'if test nil (cons 'do body)))

Macros

Debugging can be hard

(macroexpand '(when-not true false))
;; (if true nil (do false))
(macroexpand-1 '(when true (println "hello world")))
;; (if true (do (println "hello world"))

Macros

Not around at runtime

(defmacro thing [x] (list 'if x x))  ;; silly example
(macroexpand '(thing 10)
;; (macroexpand '(thing false))
(map thing '(true false 10 "Hello")) ;; throws CompilerException

Macros

Issues

Benefits

What makes clojure functional?

From math.model-metrics

(defn- regression [betas signals]
  (dot-product (cons 1 signals) betas))
(defn- logistic-regression
  "Compute 1/(1 + e^(-regression(beta, signals)))."
  [beta signals]
  (/ 1
      (+ 1
         (Math/exp (- (regression beta signals))))))

What makes clojure functional?

(defn- make-predictions
  [signals betas forecast]
  (forecast betas signals))
(make-predictions signals betas regression)
(make-predictions signals betas logistic-regression)

Examples from the Intent Media code base

From extract.signals

(defn sites->features[site-type]
  (sort-by (fn [x] (.name x))
           (concat generic-features
                   (specific-features site-type))))

Destructuring in Clojure

Abstract structural bindings

(def point [5 7])

(let [[x y] point]
  (println "x:" x " y:" y)) ;; x: 5 y: 7


(def point {:x 5 :y 7})

(let [{x :x y :y} point]
  (println "x:" x "y:" y))  ;; x: 5 y: 7

(let [{:keys [x y]} point]
  (println "x:" x "y:" y))  ;; x: 5 y: 7

Examples from the Intent Media code base

From tasks.generate-signals

Examples from the Intent Media code base

(defn -main [& args]
  (let [arg-map (parse-args args)
        {:keys [-stepInputBaseUrl
                -outputUrl
                -testingDateFrom
                -testingDateTo
                -trainingDateFrom
                -trainingDateTo
                -siteType
                -productCategoryType
                -publisherType
                -dataType]} arg-map
        base-input-path (UrlHelper/decode -stepInputBaseUrl)
        output-path (UrlHelper/decode -outputUrl)
        publisher-type (PublisherType/valueOf -publisherType)
        site-type (SiteType/valueOf -siteType)]
        ;; some method call
        ))

Thank you


Questions?



Try Clojure in your browser, http://tryclj.com/

Credits

Rich Hickey image from a blog

S-expr image from SICP