java 2020-10-28

@jimka.issy has joined the channel

Hi everyone. I have some questions about objects which respond true to the clojure class? function. First of all what is the correct word to use to describe the set of objects for which class? returns true? I want to call them "classes" but sometimes people complain that interfaces are not classes.

The Java Virtual Machine specification (separate from the Java language specification, which specifies Java source code) uses the names classes and interfaces, e.g. you can find occurrences of both in the table of contents here: https://docs.oracle.com/javase/specs/jvms/se8/html/

The Java documentation for the class java.lang.Class says near the beginning: "Instances of the class `Class` represent classes and interfaces in a running Java application." -- https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html

interesing. so the word "Class" with a capital C includes classes and interfaces ?

Alex Miller (Clojure team) 2020-10-28T15:13:01.016400Z

class is used both as a generic name (including interfaces) and as a specific thing (concrete classes distinct from interfaces) depending on context, but yes Class includes both

And the phrase "class and interface" and "class or interface" is sprinkled all over that last documentation page I linked.

the set of objects of class java.lang.Class ?

Alex Miller (Clojure team) 2020-10-28T15:14:19.018Z

there are also arrays, which are objects but somewhat outside typical classes, and primitives

it's hard to talk about the world with a limited number of words which different people attach different semantics to.

Alex Miller (Clojure team) 2020-10-28T15:15:07.018600Z

ain't that the truth

Hence the use of short phrases and definitions of terms within a document that needs to make precise distinctions that are not often made casually.

OK, 2nd question. Can someone give me an example of two interfaces (which I can reference by name in clojure such as java.lang.Comparable or .Serializable) which are incompatible in the sense that they both have a member with the same name and the same parameter types?

i.e. two interfaces which are guaranteed never to be simultaneously in the ancestors list of any one Class (with a capital C)

If no one knows, one way to attempt to answer the question is to run some code that scans the JVM's internal state to try to find all current instances of java.lang.Class, keeps only the ones that represent interfaces, and write some code that collects together all of their method names and signatures, looking for similar ones.

I have some Clojure code somewhere I can probably link to that uses a library written by someone else to look for all (or at least most) instances of java.lang.Class in a running JVM. I haven't written code to do the latter part, but it should be straightforward to write given a collection of instances of java.lang.Class

I tried to find the descendants of Object, but that doesn't work. If it did I could find all descendants, then fall all their ancestors. Since interfaces won't be descendants, but gathering all the ancestors would find a huge number of interfaces.

@andy.fingerhut indeed, given a collection of a large number of instances of java.lang.Class, yes I would have a good chance of finding such interfaces.

The JVM set of classes and interfaces is by design very dynamic, and they can be loaded/created by a myriad of different ClassLoader objects, and there are security mechanisms to try to hide the existence of some from different "places" in the system, so there are varying degrees of completeness you can achieve with such a list, depending on how you do it.

It might be an hour or three before I find the code I wrote (away from that laptop right now)

Alex Miller (Clojure team) 2020-10-28T15:30:15.029700Z

you could skim the javadoc index

Alex Miller (Clojure team) 2020-10-28T15:32:29.030500Z

like abort() is a "method in interface" for many interfaces, probably not all the same hierarchy

Alex Miller (Clojure team) 2020-10-28T15:33:16.031Z

http://java.net.http.WebSocket and javax.security.auth.spi.LoginModule

Alex Miller (Clojure team) 2020-10-28T15:35:30.031300Z

accept(<foo>)

Alex Miller (Clojure team) 2020-10-28T15:37:02.031500Z

add(<foo>)

Alex Miller (Clojure team) 2020-10-28T15:37:13.031800Z

probably pick any common verb :)

http://java.net.http.WebSocket seems to be both :abstract and also :interface

Alex Miller (Clojure team) 2020-10-28T15:37:59.032400Z

all interfaces are abstract

ah ha, but not all abstracts are interfaces?

Alex Miller (Clojure team) 2020-10-28T15:38:18.032900Z

correct

Alex Miller (Clojure team) 2020-10-28T15:38:44.033700Z

abstract classes can't be instantiated

Alex Miller (Clojure team) 2020-10-28T15:39:12.034200Z

they have to be subclassed and made non-abstract (usually by overriding abstract methods)

what does it mean if a Class is not :abstract, and not :interface, and not :final ?

does it work the same as abstract but can be instantiated?

If it is none of those, that is probably the most common kind of class that people learn to create in Java first. It is a class, not an interface. It is not declared abstract, so you can use new to construct instances of that class. It is not final, so you can define subclasses of it if you wish.

interface -> it is a Java interface (implies abstract, and I believe never final), abstract -> cannot construct instances of it. final -> cannot declare subclasses of it

ahhh, so these are common ?

not exotic?

Having none of those three flags present is common

do we have examples of those in clojure other than Object ?

Object is the one that I have seen. and I've made a special case of it. but if this is common, I need to understand it better, perhaps.

clojure.lang.PersistentVector

java.lang.Object is in every JVM, whether Clojure is running or not. It is the class from which all other classes are sub-classes (perhaps through a chain of multiple other intermediate classes)

great.. is there some class which inherits from clojure.lang.PersistentVector ?

If you go through the source directory here, you can probably find a bunch, all in the clojure.lang package: https://github.com/clojure/clojure/tree/master/src/jvm/clojure/lang

ie. a subclass of clojure.lang.PersistentVector ?

I do not know of any off hand. Perhaps not in Clojure itself, but some third party library very well might.

again if it were possible to walk the instances of java.lang.Class I could programmatically find such cases.

@jimka.issy FWIW, most things that need to be "vector-like" extend APersistentVector which is:

public abstract class APersistentVector extends AFn implements IPersistentVector, Iterable,
                                                               List,
                                                               RandomAccess, Comparable,
                                                               Serializable, IHashEq {

Then PersistentVector extends that and implements a few extra interfaces:

public class PersistentVector extends APersistentVector implements IObj, IEditableCollection, IReduce, IKVReduce{

clojure-rte.rte-core> (:flags (refl/type-reflect clojure.lang.PersistentVector))
#{:public}
clojure-rte.rte-core> (:flags (refl/type-reflect clojure.lang.APersistentVector))
#{:public :abstract}
clojure-rte.rte-core> (:flags (refl/type-reflect clojure.lang.IPersistentVector))
#{:interface :public :abstract}
clojure-rte.rte-core> 

Can someone suggest a better name for this function? The function takes a class-name such as the symbol Number , uses resolve to get the class and then uses (comp :flags efl/type-reflect) to get a list of flags. Then returns a keyword from :abstract, :interface, :final, :public . My function has a really bad name at the moment. I need a better name for this.

(defn-memoized [class-type -class-type]
  "Takes a class-name and returns either :abstract, :interface, :public, or :final,
  or throws an ex-info exception."
  [t]
  (let [c (find-class t)
        r (refl/type-reflect c)
        flags (:flags r)]
    (cond
      (= c Object) ; case #1
      :abstract
      (contains? flags :interface) ; case #2
      :interface
      (contains? flags :final) ; case #3
      :final
      (contains? flags :abstract) ; case #4
      :abstract
      (= flags #{:public}) ; case #5
      :public
      
      :else
      (throw (ex-info (format "disjoint? type %s flags %s not yet implemented" t flags)
                      {:error-type :invalid-type-flags
                       :a-type t
                       :flags flags})))))
BTW, the function makes a special case of Object, when I'm still not sure is correct for my application. The more I think about it, the more I think case 1 and 5 should be merged into a single case. That might change in the future when I understand the situation better.

Alex Miller (Clojure team) 2020-10-28T17:31:04.049700Z

all of the A... types in clojure are generally base classes, I would guess most are abstract

and and I... classes are interfaces?

Alex Miller (Clojure team) 2020-10-28T19:25:52.050500Z

yes, that's a common Java idiom

Alex Miller (Clojure team) 2020-10-28T19:26:03.050700Z

(the A... is not, but it's common in the Clojure code base)

Alex Miller (Clojure team) 2020-10-28T19:26:33.050900Z

which is not to say that it's totally followed - things like Countable are interfaces too

@seancorfield do you know of a subclass of clojure.lang.PersistentVector)

Nothing within Clojure itself -- that's what I was just looking for in the Java source code. I'd be very surprised to find code even out in the wild that extends PersistentVector. A search on GitHub found code that extends APersistentVector and code that wraps PersistentVector (using it as an implementation detail inside some other "persistent" object).

such would be great for a test case of my code

I found an interesting one:

clojure-rte.rte-core> (:flags (refl/type-reflect BigInteger))
#{:public}
clojure-rte.rte-core> 

What's "interesting" about BigInteger @jimka.issy?

I found some code that I have used in the past to list most/all classes in a running JVM, but in trying to run it today I seem to have lost the recipe for what command line options when starting the JVM it requires to have the right privileges to work properly. It is some test code in this repository: https://github.com/jafingerhut/cljol

A tiny shell script that might work with some operating system / JDK versions, or did at one point, is in the file doc/generate-report.sh

it uses this library to do the actual finding of Java classses and I believe also interfaces: https://github.com/classgraph/classgraph

Here is a short sample use of calling the classgraph library from Clojure, with no other libraries required except classgraph and those included with Clojure: https://github.com/jafingerhut/cljol/blob/master/doc/sample-classgraph-use.clj