1

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

dilvan
  • 2,109
  • 2
  • 20
  • 32

2 Answers2

1

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.

Taylor Wood
  • 15,886
  • 1
  • 20
  • 37
  • Documentation for the ASeq class is also missing. What does the method public Obj withMeta(IPersistentMap iPersistentMap) do? – dilvan Feb 28 '18 at 19:01
  • @dilvan it takes a map of metadata and returns an update instance with that metadata attached. See [`with-meta`](http://clojuredocs.org/clojure.core/with-meta) and related functions for more. – Taylor Wood Feb 28 '18 at 19:26
  • My [implementation for ISeq](https://gist.github.com/dilvan1/6d1b0a4d487b21d925a47748664e130a) in Java (comments are welcomed). – dilvan Feb 28 '18 at 21:38
1

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, Iterables 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.

Michał Marczyk
  • 83,634
  • 13
  • 201
  • 212
  • I want to add support to my java classes and avoid having to copy data to an array to then create a Clojure data structure. – dilvan Feb 28 '18 at 19:11
  • 1
    Clojure can deal with many Java collection interfaces directly. `Iterable`s can be `reduce`'d efficiently (I mentioned this in the answer), `RandomAccess` instances are directly supported by `nth`, Java maps are supported by `get` and `contains?` &c. So, assuming your Java classes implement the expected Java interfaces, it is very likely that they will "just work" in Clojure without any preparatory copying into Clojure structures or arrays. Indeed it is quite likely that they will work efficiently. (Again, you may expect no extra allocations if you `reduce` / `transduce` your `Iterable`s &c.) – Michał Marczyk Mar 01 '18 at 01:01