Slide 1

Slide 1 text

Clojure in a Java shop Chris Mowforth | @m0wfo Logentries

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

Why? ● REPL ● Compact core ● Homoiconicity ● Macros ● Java Interop ● Concurrency

Slide 4

Slide 4 text

Lisp on the JVM ● Where? ○ Stateless [micro]services ○ Monitoring existing code ○ Prototyping ○ Data migrations / general housekeeping ○ Testing

Slide 5

Slide 5 text

Lisp on the JVM ● Build tools ● Java interop ● Clojure interop ● Testing ● Dependency injection ● Design patterns

Slide 6

Slide 6 text

Build Tools tl;dr: You don’t have to use Leiningen :) clojars.org http://clojars.org/repo Maven: Clojars repo + Clojure Maven Plugin mvn clojure:repl +

Slide 7

Slide 7 text

Java Interop (import '[java.util.concurrent Executors Future]) Mostly intuitive (let [str (JOptionPane/showInputDialog "hi")] (println str))

Slide 8

Slide 8 text

Java Interop Work with Java objects almost transparently (def a (java.util.ArrayList.)) (. a addAll [1 2 3]) (def mapped (pmap #(inc %) a)) ; => (2 3 4) -LazySeq, not an ArrayList! (class mapped) (def pool (Executors/newCachedThreadPool)) ; clojure.lang.IFn extends Runnable & Callable (. pool execute #(println “hi from pool!”))

Slide 9

Slide 9 text

Java Interop Interfaces ; Executor impl backed by Clojure futures (deftype MyExecutor [] java.util.concurrent.Executor (execute [this runnable] (future (. runnable run)))) (let [exec (MyExecutor.)] ; submit a job (. exec execute #(println "Hello, world!")))

Slide 10

Slide 10 text

Java Interop Extend & compose types, retain encapsulation class Array def monkey_patch # oh god... end end

Slide 11

Slide 11 text

Java Interop Extend & compose types, retain encapsulation (defprotocol IMessage "Contract for messaging clients" (send-msg [this msg] "Send a message") (extend-type com.example.ZMQImpl (send-msg [this ^String msg] ; implementation )) (send-msg (ZMQImpl.) "a message")

Slide 12

Slide 12 text

Clojure Interop ns and gen-class are your friends (ns com.example.app (:gen-class :name com.example.app.MyActor :extends akka.actor.UntypedActor :methods [[onReceive [Object] void]])) (defn -onReceive [msg] (println "I'm in an actor!")) (needs to be compiled AOT)

Slide 13

Slide 13 text

Testing (defn increment [x] (+ x 1)) ; doesn’t get much simpler: (deftest test-increment (is (= 2 (inc 1)))) clojure.test If you use x test lib in Java, chances are it’ll work fine.

Slide 14

Slide 14 text

D.I Is a bit one-sided... ; set up some singletons from a Guice module (let [module (BootstrapModule.) injector (Guice/createInjector module) dao (. injector getInstance (class DaoImpl))] ; ... and so on... ) ...fine for external dependencies, no clear way to manage fns though.

Slide 15

Slide 15 text

D.I With-redefs reduces coupling... (def x 1) ; Override top-level binding on x (with-redefs [x 2] (println x)) ; => 2 (println x) ; => 1 ...but still doesn’t manage object graph. TBC.

Slide 16

Slide 16 text

Design Patterns Use macros ; write your own language primitives (defmacro try-with-resources [closeable & body] `(let ~closeable (try ~@body (finally (. ~(first closeable) close))))) (try-with-resources [c (FileReader. "/home/chris/somefile.txt")] (println (. c ready))

Slide 17

Slide 17 text

Design Patterns Isolate mutable state Use observer / future / channel abstractions (c.f. rxJava, core.async, aleph, e.t.c) (import 'rx.Observable) (Observable/create (fn [subscriber] (while true (let [delivery (. consumer nextDelivery)] (. subscriber onNext (. delivery getBody)))))

Slide 18

Slide 18 text

Design Patterns core.typed? optional static typing- still under heavy development

Slide 19

Slide 19 text

Summary Using Clojure in a Java codebase is a viable proposition. ● Use whatever build toolchain you would normally ● Java interop pretty smooth ● Java can consume your Clojure code- not a black box ● Test as you would in Java ● Focus on solving hard problems, not ceremony (syntax, configuration, …)

Slide 20

Slide 20 text

Questions?