Clojure Lisp for the Real World - YOW! Conferences · 2019-09-23 · Clojure Values 3 Long 6.022e23...

Post on 15-Mar-2020

8 views 0 download

Transcript of Clojure Lisp for the Real World - YOW! Conferences · 2019-09-23 · Clojure Values 3 Long 6.022e23...

ClojureLisp for the Real World

@stuartsierraclojure.com

Stuart SierraRelevance, Inc.

Clojure/core

Clojure contributor

Values

Values

3

Values

3 + 2 = 5

Values

3let x =

Values

3let x =

let x = 5

Values

3let = 5

Values

3define = 5

Values

3

Values

3Immutable

Values

3Immutable Persistent

He ll o, world!" "

H e ll o , w o r l d ! \0

greeting

char greeting[14];strcpy(greeting, "Hello, World!");

H e ll o , w o r l d ! \0

greeting

char* name = greeting + 7;

name

H e ll o , b o r e d ! \0

greeting

name[0] = 'b';name[3] = 'e';

name

String greeting = new String("Hello, world!");

H e ll o , w o r l d !

java.lang.String

greeting

H e ll o , w o r l d !

java.lang.String

greeting

H e ll o , b o r e d !

java.lang.String

String alt = greeting.replace("world", "bored");

alt

H e ll o , w o r l d !

java.lang.String

greeting

Immutable

In the real world,values don't change.

Clojure Values

3 Long

6.022e23 Double

"Hello, world!" String

3.0M BigDecimal

9223372036854775808N BigInt

2/3 Ratio

Clojure Values

even? Symbol

:last-name Keyword

(print 99 "bottles") List

[3.14 :pi] Vector

{:x 3, "y" 4} Map

#{7 9 "foo"} Set

Clojure Values

ListO(1) at the head

O(n) anywhere else

Vector O(1) access

Map O(1) access

Set O(1) contains?

*

*

*

Immutable Persistent

Clojure Values(1 2 3)

[:a :b :c]

{:a 4}

#{3 7}

x

...

y x

...

x

...

x

...

y

...

x y

...

...

...

...

...

...

...

Clojure Values

ListO(1) at the head

O(n) anywhere else

Vector O(log32n) access

Map O(log32n) access

Set O(log32n) contains?

321 = 32322 = 1024323 = 32,768324 = 1,048,576325 = 33,554,432326 = 1,073,741,824327 = 34,359,738,368

log32 1000 < 2log32 10,000 < 3

log32 1,000,000 < 4log32 10,000,000 < 5

log32 1,000,000,000 < 6

In the real world,log32n is fast enough.

Code is Data

#{ {:first "Stuart" :last "Sierra"} {:first "Luke" :last "VanderHart"} {:first "Michael" :last "Fogus"} }

Code is Data

#{ {:first "Stuart" :last "Sierra"} {:first "Luke" :last "VanderHart"} {:first "Michael" :last "Fogus"} }

Code is Data

A set of maps

(+ 3 4 5)List

Symbol

(+ 3 4 5)List

Symbol

Function call

(defn average [& nums] (/ (reduce + nums) (count nums)))

(average 3 4 5)

ListSymbols

VectorSymbols

Function call

Host Interop.

(def m (ConcurrentHashMap. 100))

(.put m "key" "value")

constructor

method call

You

Lexer/Parser

Characters

AbstractSyntax Tree

Compiler/Interpreter

Clojure Reader

ClojureCompiler

You Characters

Data Structures

JVM Bytecode

Clojure Reader

You Characters

Data Structures

Macros

ClojureCompiler

JVM Bytecode

(let [file ...initializer...] (try ... do something ... (finally (.close file))))

(defmacro with-open [bindings & body] `(let ~bindings (try ~@body (finally (.close ~(first bindings)))))

(let [file ...initializer...] (try ... do something ... (finally (.close file))))

(with-open [file ...initializer...] ... do something ...)

Generic Data Access

#{ {:first "Stuart" :last "Sierra"} {:first "Luke" :last "VanderHart"} {:first "Michael" :last "Fogus"} }

Generic data access

A set of maps

Author[] authors = ...

String names[] = new names[authors.length];for (int i = 0; i < authors.length; i++) { names[i] = authors[i].getFirstName();}

(defn get-first-name [author] (get author :first))

(map get-first-name authors)("Stuart" "Luke" "Michael")

Higher-orderfunction

(map (fn [a] (get a :first)) authors)("Stuart" "Luke" "Michael")

Anonymous function

(map #(get % :first) authors)("Stuart" "Luke" "Michael")

Anonymous function

(map #(% :first) authors)("Stuart" "Luke" "Michael")

Maps are functions

(map #(:first %) authors)("Stuart" "Luke" "Michael")

Keywords are functions!

(map :first authors)("Stuart" "Luke" "Michael")

Keywords are functions

(map :first authors)

String names[] = new names[authors.length];for (int i = 0; i < authors.length; i++) { names[i] = authors[i].getName();}

vs.

(map function set-of-maps)

(map function set-of-maps)(map function vector-of-sets-of-maps)(map function list-of-vectors)(map function map-of-maps)

(map function set-of-maps)

(map function (resultset-seq query))

(map function (line-seq file))(map function (xml-seq xml-tree))

(map function (file-seq directory))

(map function list-of-vectors)(map function map-of-maps)

(map function vector-of-sets-of-maps)

mapfilter

reducecountsome

removereplace

Sequence API

and lots more...

ListVectorMap

ResultSetStream

DirectoryIterator

XML

and lots more...

Sequence Generators

mapfilter

reducecountsome

removereplace

Sequence API

and lots more...

ListVectorMap

ResultSetStream

DirectoryIterator

XML

and lots more...

Sequence Generators

× = Power

Polymorphism

or, OOP: the Good Parts

Multimethods(defmulti encounter (fn [a b] ... food chain comparison ...))

(defmethod encounter [:predator :predator] [a b] (fight a b))

(defmethod encounter [:predator :prey] [a b] (eat a b))

(defmethod encounter [:prey :predator] [a b] (eat b a))

(defmethod encounter [:prey prey] [a b] (run-away a) (run-away b))

Protocols(defprotocol Coercions "Coerce between various 'resource-namish' things." (as-file [x] "Coerce argument to a file.") (as-url [x] "Coerce argument to a URL."))

(extend-protocol Coercions nil (as-file [_] nil) (as-url [_] nil) String (as-file [s] (File. s)) (as-url [s] (URL. s)) File (as-file [f] f) (as-url [f] (.toURL (.toURI f))))

Records(def me {:name "Stuart" :state "NY" :city "Brooklyn"})

(type me) clojure.lang.PersistentHashMap

(:city me) "Brooklyn"

(defrecord Person [name state city])

(def me (->Person "Stuart" "NY" "Brooklyn"))

(type me) user.Person

(:city me) "Brooklyn"

Record + Protocols(defprotocol Coercions "Coerce between various 'resource-namish' things." (as-file [x] "Coerce argument to a file.") (as-url [x] "Coerce argument to a URL."))

(defrecord MyResource [protocol host path] Coercions (as-file [x] (java.io.File. path)) (as-url [x] (java.io.URL. (str protocol "://" host "/" path))))

Records(defrecord Person [name state city])

(def me (->Person "Stuart" "NY" "Brooklyn"))

(type me) user.Person

(:city me) "Brooklyn"

(ancestors (type me)) #{clojure.lang.IPersistentCollection java.io.Serializable clojure.lang.Associative clojure.lang.IPersistentMap clojure.lang.IKeywordLookup clojure.lang.IMeta clojure.lang.IObj java.util.Map java.lang.Iterable clojure.lang.Counted clojure.lang.IRecord clojure.lang.ILookup java.lang.Object clojure.lang.Seqable}

Types

(deftype SuperNewTree [size root])

(def me (->SuperNewTree 0 nil))

(type me) user.SuperNewTree

(ancestors (type me)) #{clojure.lang.IType java.lang.Object}

Existing Protocolsor Interfaces

Your newprotocol here

Existing Types

Existing Method Implementations

extend(very hard in Java)

Your new type here

extend

The Expression Problem

Concurrency

Concurrency

Parallelism

State

class Invoice { private Date date;

public Date getDate() { return this.date; }

public void setDate(Date date) { this.date = date; }}

class Date { public void setDay(int day); public void setMonth(int month); public void setYear(int year);}

Mutable!

class Invoice { private Date date;

public Date getDate() { return this.date; }

public void setDate(Date date) { this.date = date; }}

Better notchange it!

Better notchange it!

class Invoice { private Date date;

public Date getDate() { return this.date.clone(); }

public void setDate(Date date) { this.date = date.clone(); }}

Defensivecopying

Programming with immutable values

means never having to say you're sorry.

today

June 20

today

June 20 June 21

Identity

State

today

Value Value

June 20 June 21

Identity

State

today

function

Value Value

functionJune 20 June 21

Identity

State

Past

function

Present Future

function

today

June 20 June 21June 19

June 20

Identity

State

Past

June 21June 19 function

Present Future

function

today

The future is a function of the past.

Mutable References

Ref Atom

Var Agent

readers

Atomwriter

writer

abortretry spin

commit

Atom(def tick (atom 1))(deref tick) 1

tick

1

Atom(def tick (atom 1))(deref tick) 1

(swap! tick inc)@tick 2

tick

1

2

inc

Atom(def tick (atom 1))(deref tick) 1

(swap! tick inc)@tick 2

(swap! tick + 10)@tick 12

tick

2

12

+10

value value value value

value value value value

value value value value

identity

identityidentity

value value value value

value value value value

value value value value

identity

identityidentity

Refwriter

writer

readers

abortretryRef

transaction

wait

commit

Ref(def A (ref 1))(def B (ref 10))

A 1

10B

Ref(def A (ref 1))(def B (ref 10))

(dosync (alter A inc) (alter B + 10))

A 1

10

20

inc

+10

B

2

transaction

Ref(def A (ref 1))(def B (ref 10))

(dosync (alter A inc) (alter B + 10))

A

20

B

2

@A 2@B 20

AtomicConsistentIsolatedDurable

Database Transactions

AtomicConsistentIsolated

Clojure Transactions

Agentactionaction

readers

action queue

Agent Thread Pool

Agent(def A (agent 1))

A 1queue

Agent(def A (agent 1))

(send A inc)@A 1

A 1inc

queue

Agent(def A (agent 1))

(send A inc)@A 1

A 1inc

2@A 2

queue

Agents != Actors

Agents != Actors

Agent Actor

Reads

Actions / Messages

Distributed?

Cheap,do not block

Send message,wait for response

Any functionOnly what receiver

understands

No Yes

Agents != Actors

Agent Actor

Reads

Actions / Messages

Distributed?

Cheap,do not block

Send message,wait for response

Any functionOnly what receiver

understands

No Yes

Agents != Actors

Agent Actor

Reads

Actions / Messages

Distributed?

Cheap,do not block

Send message,wait for response

Any functionOnly what receiver

understands

No Yes

Agents != Actors

Agent Actor

Reads

Actions / Messages

Distributed?

Cheap,do not block

Send message,wait for response

Any functionOnly what receiver

understands

No Yes

Ref Atom

Var Agent

Concurrency

• No locking in user code

• No deadlocks

• Guaranteed forward progress

• Writes never block reads

More

• Me: stuartsierra.com,@stuartsierra

• Us: thinkrelevance.com,clojure.com

• Clojure: clojure.org

Image Credits: openclipart.org, pdtextures.blogspot.com,Clojure logo by Tom Hickey