Reputation: 2239
I want to pass to Clojure a map of Java functions. The functions use Java anonymous classes that implement the IFn interface. I also need to implement the ISeq interface to pass lists. The ISeq interface is simple but has no documentation. I need to know what each method in ISeq does and/or find Java code of a class that implements all ISeq methods (I do not want to copy my lists to Clojure structures).
Upvotes: 1
Views: 471
Reputation: 84351
This fragment of the question text appears to suggest that calling seq
on an array, which is what the linked answer to a question about converting Java arrays to Clojure seqs suggests, copies the contents of the array to a fresh seq:
(I do not want to copy my lists to Clojure structures)
This is not the case. What happens instead is that seq
returns a wrapper seq backed by the array, as evidenced by the following REPL interaction:
Clojure 1.9.0
user=> (def xs (object-array [:foo :bar]))
#'user/xs
user=> (seq xs)
(:foo :bar)
user=> (def s1 (doall (seq xs)))
#'user/s1
user=> s1
(:foo :bar)
user=> (aset xs 0 :quux)
:quux
user=> (seq xs)
(:quux :bar)
user=> s1
(:quux :bar)
Notice that mutating the underlying array results in an observable change to the seq on the array that was created before the mutation.
This contrasts with the behaviour of seq
when passed a java.util.List
that is not simultaneously an ISeq
, in which case a fresh seq will indeed be allocated:
Clojure 1.9.0
user=> (def ys (java.util.ArrayList. [:foo :bar]))
#'user/ys
user=> ys
[:foo :bar]
user=> (def s2 (seq ys))
#'user/s2
user=> s2
(:foo :bar)
user=> (.set ys 0 :quux)
:foo
user=> ys
[:quux :bar]
user=> s2
(:foo :bar)
This need not be a terrible outcome, as the seq will be chunked, but it may make sense to avoid any wrapping.
Even then, however, the exact usage pattern should be examined, as it is entirely possible that the Java lists could be passed to Clojure directly and processed without any intermediate seq allocations. For example, Iterable
s can be reduce
'd directly and efficiently with no such allocations.
So, I would reexamine the assumption that the lists that need to be transferred from the Java side to Clojure need cross the divide as ISeq
instances. This is not to say that under no circumstances can such an arrangement make sense – merely that from the limited detail provided in the question text it doesn't follow that this is necessary in this particular case.
As for how to implement ISeq
if you decide to do so in the end, I concur with Taylor Wood's suggestion to examine clojure.lang.ASeq
.
Upvotes: 1
Reputation: 16194
I need to know what each method in ISeq does
You should also understand the interfaces it extends:
public interface Seqable {
ISeq seq(); // returns an ISeq instance
}
public interface IPersistentCollection extends Seqable {
// returns the count of this coll
int count();
// returns a new coll with o cons'd onto it
IPersistentCollection cons(Object o);
// returns a new empty coll (of same type?)
IPersistentCollection empty();
// compares this coll against o for equality
boolean equiv(Object o);
}
public interface ISeq extends IPersistentCollection {
// returns the first item in this seq
Object first();
// returns a new seq of every item in this except the first
ISeq next();
// returns a new seq of every item in this except the first
// ASeq returns empty for empty
ISeq more();
// returns a new seq with o cons'd onto this seq
ISeq cons(Object o);
}
...or find Java code of a class that implements all ISeq methods
The ASeq
abstract class implements (most of) ISeq
. Classes that extend ASeq
implement ISeq.next()
.
There's surely an easier way to accomplish your goal besides reimplementing ISeq
. Maybe looking at Clojure's Java implementation will give you some ideas.
Upvotes: 1