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

Cljmuc: Clojure/Java interop

αλεx π
June 09, 2012
460

Cljmuc: Clojure/Java interop

Clojure/Java interoperability talk from Clojure Munich User Group.

αλεx π

June 09, 2012
Tweet

Transcript

  1. Disclaimer #2 one doesn’t come to Clojure to escape Java.

    Writing complex systems will require you to use strong sides of a complete infrastructure and have interoperability on all levels, within JVM and across the applications running on different platforms. kthxbai Saturday, June 9, 12
  2. Disclaimer #3 Clojure won’t magically solve your concurrency problems STM

    is not panacea, and should be used with care and diligence. Although Clojure will teach you to compose things rather than build inheritance chains, avoid side effects and utilize maximum of lang core. It’s not Scala, so you can’t write your Java in Clojure. Saturday, June 9, 12
  3. Hickey paradigms - prefer interface polymorphism over concrete inheritance -

    datatypes only expose their methods in protocols and interfaces - immutability is a preferred default (didn’t we want to make it happen in our Java code anyways?) - tying polymorphism to inheritance is bad (extending types with protocols help you to avoid it) - prefer composition over inheritance (but that’s more of a clojuric thing) Saturday, June 9, 12
  4. Method calls (.. System (getProperties) (get "os.name")) = (. (.

    System (getProperties)) (get "os.name")) = (-> (System/getProperties) (.get "os.name")) Saturday, June 9, 12
  5. Setters (let [mo (MongoOptions.)] (set! (. mo maxWaitTime) max-wait-time) (set!

    (. mo connectTimeout) connect-timeout) (set! (. mo socketTimeout) socket-timeout) (set! (. mo socketKeepAlive) socket-keep-alive)) Saturday, June 9, 12
  6. Type Hints (ns monger.core (:import [com.mongodb Mongo DB])) (defn ^DB

    get-db ([^String name] (.getDB *mongodb-connection* name)) ([^Mongo connection, ^String name] (.getDB connection name))) Saturday, June 9, 12
  7. (let [synth (. MidiSystem getSynthesizer) channel (first (. synth getChannels))

    frame (JFrame. "Music Frame")] (. frame setSize 300 300) (. frame setDefaultCloseOperation JFrame/EXIT_ON_CLOSE) (. frame addKeyListener (proxy [KeyListener] [] (keyPressed [e] (. channel noteOn (int (.getKeyChar e)) 64)) (keyReleased [e] (. channel noteOff (int (.getKeyChar e)))) (keyTyped [e]))) (. frame setVisible true)) Saturday, June 9, 12
  8. (doto x & forms) Evaluates x then calls all of

    the methods and functions with the value of x supplied at the front of the given arguments. The forms are evaluated in order. Returns x. Saturday, June 9, 12
  9. (doto frame (. setSize 300 300) (. setDefaultCloseOperation JFrame/EXIT_ON_CLOSE) (.

    addKeyListener (proxy [KeyListener] [] (keyPressed [e] (. channel noteOn (int (.getKeyChar e)) 64)) (keyReleased [e] (. channel noteOff (int (.getKeyChar e)))) (keyTyped [e]))) (. setVisible true)) Saturday, June 9, 12
  10. (->) Threads the expr through the forms. Inserts x as

    the second item in the first form, making a list of it if it is not a list already. If there are more forms, inserts the first form as the second item in second form, etc. Saturday, June 9, 12
  11. ;; clj-http (def request (-> #'core/request wrap-redirects wrap-exceptions wrap-decompression wrap-input-coercion

    wrap-output-coercion wrap-query-params wrap-basic-auth wrap-accept wrap-accept-encoding wrap-content-type wrap-method wrap-url)) Saturday, June 9, 12
  12. (->>) Threads the expr through the forms. Inserts x as

    the last item in the first form, making a list of it if it is not a list already. If there are more forms, inserts the first form as the last item in second form, etc. (basically, same thing as -> just backwards) Saturday, June 9, 12
  13. ;; refheap (defn preview "Get the first 5 lines of

    a string." [s] (->> s StringReader. io/reader line-seq (take 5) (string/join "\n"))) Saturday, June 9, 12
  14. (proxy) Expands to code which creates a instance of a

    proxy class that implements the named class/interface(s) by calling the supplied fns. A single class, if provided, must be first. If not provided it defaults to Object. Saturday, June 9, 12
  15. proxy - extending existing base class - clojure -> java

    interop - anonymous type is enough (no need in named type) Saturday, June 9, 12
  16. package java.io; public abstract class InputStream implements Closeable { public

    abstract int read() throws IOException; public int read(byte b[]) throws IOException { return read(b, 0, b.length); } public int read(byte b[], int off, int len) throws IOException { // ... return i; } // ... } java Saturday, June 9, 12
  17. (io/copy (proxy [InputStream] [] (read [buffer] (let [bytes (.read stream

    buffer)] (update-file file-upload-id :uploaded-size bytes) bytes))) temp-file) clojure Saturday, June 9, 12
  18. (reify) Defines both an anonymous type and creates an instance

    of that type. The use case is where you need a one-off implementation of one or more protocols or interfaces and would like to take advantage of the local context. In this respect it is use case similar to proxy, or anonymous inner classes in Java. Saturday, June 9, 12
  19. reify - clojure -> java interop - anonymous type is

    enough (no need in named type) - preferred, unless semantics force you to use proxy - only protocols and interfaces supported, no concrete superclass - method bodies real fns of resulting class, not external fns - invocation is direct, without map lookup - no support for dynamic method swapping - better performance - returns instance of implementation Saturday, June 9, 12
  20. package com.rabbitmq.client; import java.io.IOException; public interface ReturnListener { void handleReturn(int

    replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException; } java Saturday, June 9, 12
  21. (defn return-listener [^clojure.lang.IFn handler-fn] (reify ReturnListener (handleReturn [this, reply-code, reply-text,

    exchange, routing-key, properties, body] (handler-fn reply-code, reply-text, exchange, routing-key, properties, (String. ^bytes body))))) clojure Saturday, June 9, 12
  22. (defrecord) Dynamically generates compiled bytecode for class with the given

    name, in a package with the same name as the current namespace, the given fields, and, optionally, methods for protocols and/or interfaces. Saturday, June 9, 12
  23. defrecord - value-based equality and hash code - complete implementation

    of IPersistentMap - faster version of Map Saturday, June 9, 12
  24. lolbenchmark (defrecord RecordExample [field-1 field-2 field-3 field-4]) (let [rec (RecordExample.

    "field-1-value" "field-2-value" "field-3-value" "field-4-value")] (time (dotimes [n 100000000] (:field-1 rec)))) ;; Elapsed time: 228.128 msecs (let [map {:field-1 "field-1-value" :field-2 "field-2-value" :field-3 "field-3-value" :field-4 "field-4-value" }] (time (dotimes [n 100000000] (:field-1 map)))) ;; Elapsed time: 569.158 msecs Saturday, June 9, 12
  25. defrecord-based polymorphism* (defrecord BasicWelleCache [^String bucket ^String content-type ^Integer w])

    (extend-protocol cache/CacheProtocol BasicWelleCache (lookup [c k] (:value (kv/fetch-one (.bucket c) k))) (has? [c k] (not (empty? (kv/fetch (.bucket c) k :head-only true)))) (hit [this k] this) (miss [c k v] (kv/store (.bucket c) k v :content-type (.content-type c) :w (.w c)) c) (evict [c k] (kv/delete (.bucket c) k :w (.w c)) c) (seed [c m] (doseq [[k v] m] (kv/store (.bucket c) k v :content-type (.content-type c) :w (.w c))) c)) * yes, this is polymorphism, as you can declare another record and extend CacheProtocol with it. Here we go, polymorphism FTW! Saturday, June 9, 12
  26. (deftype) Dynamically generates compiled bytecode for class with the given

    name, in a package with the same name as the current namespace, the given fields, and, optionally, methods for protocols and/or interfaces. Saturday, June 9, 12
  27. ;; momentum-clj (deftype Connection [queue upstream cached] clojure.lang.Seqable (seq [this]

    (concat @(.cached this) (received this))) clojure.lang.IFn (invoke [this evt val] ((.upstream this) evt val))) Saturday, June 9, 12
  28. deftype - suitable for java -> clojure interop - need

    to be AOT compiled - generates named class (real thing) - fields can have type hints - can implement one or more protocols/interfaces - supports mutable fields - no functionality specified by user, only constructor Saturday, June 9, 12
  29. Protocols - dynamic polymorphism - specification-only - single type implements

    multiple protocols - cover most of multimethod cases, while giving a better abstraction - allowing independent extension of types Saturday, June 9, 12
  30. (defprotocol VcrStore (record-request! [store key value] "") (get-request [store key]

    "") (get-all [store] "") (request-recorded? [store key] "") (clear-recorded! [store])) Saturday, June 9, 12
  31. (deftype VcrMemoryStore [store-map] VcrStore (record-request! [_ options response] (clojure.core/swap! store-map

    assoc options response)) (get-request [_ key] (clojure.core/get @store-map key)) (request-recorded? [_ options] (some #(= options %) (keys @store-map))) (clear-recorded! [_] (reset! store-map {})) Saturday, June 9, 12
  32. (deftype VcrFileStore [store-map] VcrStore (record-request! [_ options response] ...) (get-request

    [_ key] ...) (request-recorded? [_ options] ...) (clear-recorded! [_] ...) Saturday, June 9, 12
  33. (defn default-get-all [s] ...) (extend-type VcrMemoryStore [store-map] VcrStore {:get-all default-get-all)

    (extend-type VcrFileStore [store-map] VcrStore {:get-all default-get-all) Saturday, June 9, 12
  34. (gen-class) When compiling, generates compiled bytecode for a class with

    the given package-qualified :name (which, as all names in these parameters, can be a string or symbol), and writes the .class file to the *compile-path* directory. Saturday, June 9, 12
  35. ;; ./project.clj (defproject interop "1.0.0-SNAPSHOT" :description "Clojure-Java interoperability examples" :dependencies

    [[org.clojure/clojure "1.3.0"]] :java-source-paths ["src/java"] :source-paths ["src/clojure"] :aot [interop.cljinterop] :main com.ifesdjeen.interop.Caller) Saturday, June 9, 12
  36. // ./src/java/interop/Caller.java package com.ifesdjeen.interop; import interop.cljinterop.SampleClass; public class Caller {

    public static void main(String[] args) { SampleClass sample = new SampleClass(); System.out.println(sample.toString()); } } Saturday, June 9, 12
  37. Monger MongoDB client for a more civilized age: friendly, flexible

    and with batteries included https://github.com/michaelklishin/monger http://clojuremongodb.info/ Saturday, June 9, 12
  38. Langohr Feature-rich Clojure RabbitMQ client that embraces AMQP 0.9.1 Model

    https://github.com/michaelklishin/langohr Saturday, June 9, 12
  39. Neocons A feature rich idiomatic Clojure client for the Neo4J

    REST API https://github.com/michaelklishin/neocons http://clojureneo4j.info/ Saturday, June 9, 12