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

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, …)