Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Clojure in a Java Shop

Clojure in a Java Shop

Clojure Ireland Feb '14 Meetup

Avatar for Chris Mowforth

Chris Mowforth

February 05, 2014
Tweet

More Decks by Chris Mowforth

Other Decks in Technology

Transcript

  1. Lisp on the JVM • Where? ◦ Stateless [micro]services ◦

    Monitoring existing code ◦ Prototyping ◦ Data migrations / general housekeeping ◦ Testing
  2. Lisp on the JVM • Build tools • Java interop

    • Clojure interop • Testing • Dependency injection • Design patterns
  3. Build Tools tl;dr: You don’t have to use Leiningen :)

    <repository> <id>clojars.org</id> <url>http://clojars.org/repo</url> </repository> Maven: Clojars repo + Clojure Maven Plugin mvn clojure:repl +
  4. 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!”))
  5. 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!")))
  6. 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")
  7. 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)
  8. 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.
  9. 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.
  10. 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.
  11. 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))
  12. 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)))))
  13. 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, …)