Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a library of Protocols and defrecords for use from Java

At the moment, I have a completely functional Clojure library which is called from Java.

The way I do this : I have a file that uses gen-class to wrap the entire API as static methods of a single class and passes data in and out in the form of IPersistentVector and IPersistentMap.

Now, however, I'm refactoring the library and putting the functionality behind various Protocols.

I have four protocols, lets call them A, B, C and D. And two defrecords, X and Y. X and Y both implement protocols A, B and C. While Y also implements D.

What do I need to do to make these available to Java? Are these automatically available as Interfaces and Classes? Or do I still have to do the equivalent of the gen-class to make them public?

If not, what is the equivalent of the gen-class :methods clause, where I define the Java types for the arguments to the methods?

Does anyone have a simple example of making Protocols and records available to Java?

like image 390
interstar Avatar asked Aug 30 '15 14:08

interstar


1 Answers

defprotocol

Every Clojure protocol is also a Java interface with the same name and methods. If I take an example from ibm developerworks, we see that :

(ns com.amalgamated)

(defprotocol Fulfillment
  (invoice [this] "Returns an invoice")
  (manifest [this] "Returns a shipping manifest"))

Is equivalent to :

package com.amalgamated;

public interface Fulfillment {
    public Object invoice();
    public Object manifest();
}

Clojure.org also has some (rather terse) information on this.

A Java client looking to participate in the protocol can do so most efficiently by implementing the protocol-generated interface. External implementations of the protocol (which are needed when you want a class or type not in your control to participate in the protocol) can be provided using the extend construct:

(extend AType   AProtocol   
 {:foo an-existing-fn
    :bar (fn [a b] ...)
    :baz (fn ([a]...) ([a b] ...)...)}   BProtocol
    {...} ...)

definterface

If you are aiming at performance, you could consider using definterface, which use is similar to the protocols. This SO post also has details about how to use it :

(definterface Foo
  [^int foo [x ^String y]]
  [^void bar [^ints is]])

definterface seem to be faster than protocols.

defrecord

Similarly, records (as well as deftype and definterface) will generate Java Classes. Again, Clojure.org/datatypes has useful information (emphasis mine) :

deftype and defrecord dynamically generate compiled bytecode for a named class with a set of given fields, and, optionally, methods for one or more protocols and/or interfaces. They are suitable for dynamic and interactive development, need not be AOT compiled, and can be re-evaluated in the course of a single session. They are similar to defstruct in generating data structures with named fields, but differ from defstruct in that: [...]

So yes if will be available from Java. Just be careful with naming.

As a side note, you may want to have a look at calling Clojure from Java.

like image 192
nha Avatar answered Oct 27 '22 09:10

nha