can clojure definterface extend another interface? - clojure

I want to translate the following code into clojure:
package com.example.orbit.hello;
import com.ea.orbit.actors.IActor;
import com.ea.orbit.concurrent.Task;
public interface IHello extends IActor
{
Task<String> sayHello(String greeting);
}
how is this possible?

You could simply define this interface Java and use it from Clojure.
If you really want to do it in Clojure, though, that is possible as well, although you'll have to use gen-interface rather than definterface:
(gen-interface
:name fully.qualified.IName
:extends [whatever.it.INeedsTo]
:methods
[[methodName
[fully.qualified.type.of.Argument1 …] ; NB. no `this' formal argument
fully.qualified.ReturnType]])
For a real-world example, have a look at this interface definition in data.avl or this one in ctries.clj (the latter uses :extends).
The <String> part of Task<String> will not appear in the Clojure version – generics are a javac construct and do not exist at runtime on the JVM.

Related

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?
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.

can I define a java interface in Clojure?

I would like to define a java interface, in clojure - (as well as implement it) - I understand implementing can be done via both proxy and gen-class, but that always assumed the interface was already defined.
You can generate a Java interface with both clojure.core/definterface and clojure.core/gen-interface. (definterface expands to a call to gen-interface.)
(ns demo.api)
(definterface Store
(^demo.api.Store buy [])
(^demo.api.Store buy [^int q])
(^demo.api.Store sell [])
(^int getQty []))
;; or
(gen-interface
:name demo.api.Store
:methods [[buy [] demo.api.Store]
[buy [int] demo.api.Store]
[sell [] demo.api.Store]
[getQty [] int]])
Sampled from this blog post.
If you want an "Interface", in the generic sense, then take a look at Clojure Protocols.
Yes you can, with definterface:
(definterface MyInterface
(^int method1 [x])
(^Bar method2 [^Baz b ^Quux q]))
Unlike in some of the examples of the link above though, you can now type hint ^ints, ^doubles etc. in Clojure.
However be aware that there is currently no way to docstring the definterface.
Unless you have a need for Java compatibility though, you should look into clojure protocols
If this is about exposing Clojure code to Java it seems the best approach is to write the interface in Java and implement it in Clojure as seen in https://github.com/puredanger/clojure-from-java.

How to add annotations to the reified class in Clojure?

I need to create a class which implements an interface and also has an annotation, and later pass an instance of this class to some API implemented in Java.
With Clojure, I could reify the interface and get an instance like this:
(reify MyInterface (method1 [this] ...))
However, reify doesn't seem to accept annotations. How may I work around this?
First of all reify returns an object not a class. If you need to generate a class with annotations, you will need to use gen-class as shown below (the example shows adding a Deprecated annotation to the class):
(gen-class :name ^{java.lang.Deprecated true} MyClass
...)
Also, you can use deftype:
(deftype ^{java.lang.Deprecated true} MyClass
...)

clojure: Can't access non-public fields in the same package

In my clojure program I cannot access package scoped fields of the java class com.foo.Foo although I am in the namespace "com.foo" (via "(ns com.foo)" at the top of my clojure program). However, public fields in com.foo.Foo are accessible.
Why?
Two problems here:
First, the namespace com.foo is compiled to a class foo_whatever in package com; it's not compiled to a class in package com.foo.
See:
user> (ns com.foo)
nil
com.foo>
nil
com.foo> (defn hello[] "hello !")
#'com.foo/hello
com.foo> (class hello)
com.foo$hello
com.foo> (ns com.foo.hello)
nil
com.foo.hello> (defn hi[] "hi !")
#'com.foo.hello/hi
com.foo.hello> (class hi)
com.foo.hello$hi
Second, when looking for constructors or fields, the Clojure compiler uses methods getConstructor and getFields from java.lang.Class, which, by spec, only return the public constructors and public fields.
So, bad luck here. It seems you won't be able to access package-protected fields.
Edit, answering comments. The best approach for accessing package-level fields in legacy Java code would be to write a class in Java that wraps the existing class and which exposes the package protected methods and fields from that class with public methods. This way, you have more control over the name and package of the generated class.
This wrapper is a small amount of Java code, and from there you can access the fields from Clojure code.

How does clojure's defrecord method name resolution work?

After defining a record and the interfaces it implements, I can call its methods either by its name or using the java interop way using the dot operator.
user=> (defprotocol Eat (eat [this]))
Eat
user=> (defrecord animal [name] Eat (eat [this] "eating"))
user.animal
user=> (eat (animal. "bob"))
"eating"
user=> (.eat (animal. "bob"))
"eating"
user=>
Under the surface, what is going on there? Are there new clojure functions being defined? What happens when there are functions you defined that share the same name (is this possible?), how are these ambiguities resolved?
Also, is it possible to "import" java methods for other java objects so that you do not need the . operator so that behavior is like above? (For the purpose, for example, of unifying the user interface)
When you define a protocol, each of its methods are created as functions in your current namespaces. It follows that you can't have two protocols defining the same function in the same namespace. It also means that you can have them in separate namespaces and that a given type can extend both[1] of them without any nameclash because they are namespaced (in opposition to Java where a single class can't implement two interfaces with homonymous methods).
From a user perspective, protocol methods are no different from plain old non-polymorphic functions.
The fact that you can call a protocol method using interop is an implementation detail. The reason for that is that for each protocol, the Clojure compiler creates a corresponding backing interface. Later on when you define a new type with inline protocol extensions, then this type will implement these protocols' backing interfaces.
Consequently you can't use the interop form on an object for which the extension hasn't been provided inline:
(defrecord VacuumCleaner [brand model]
(extend-protocol Eat
VacuumCleaner
(eat [this] "eating legos and socks"))
(.eat (VaacumCleaner. "Dyson" "DC-20"))
; method not found exception
The compiler has special support for protocol functions so they are compiled as an instance check followed by a virtual method call, so when applicable (eat ...) will be as fast as (.eat ...).
To reply to "can one import java methods", you can wrap them in regular fns:
(def callme #(.callme %1 %2 %3))
(obviously you may need to add other arities to account for overloads and type hints to remove reflection)
[1] however you can't extend both inline (at least one of them must be in a extend-* form), because of an implementation limitation