On Mon, Jun 10, 2019 at 12:04 AM eglue <atdi...@gmail.com> wrote: > This is great, Alex, thanks. (Sorry, I deleted from underneath you and > reposted to fix the title before I saw your reply...) > > The latter option, writing interfaces and classes in Java and calling w/ > the glue code is a great option. > > However one big motivator for people moving from Java to langs like Scala > and Kotlin is being able to implement those bean/record types, eg, with > such little fanfare & boilerplate (eg in Clojure, using defrecord is so > concise). >
The problem with wanting the Clojure-y version from Java is that those interfaces are generic and weird from the Java side. So either you get bean-like code familiar to Java users or generic collection/keyword interfaces that will be weird to use from Java. I heard your goal as "be friendly from Java", which means, making bean-like interfaces. On the Clojure side, it's pretty easy to macro-ize the creation of those over defrecords or whatever, so it could still be a lot less macro work, just depends how far you want to go with it. You don't get all the benefits of Clojure from Java; you have to be in Clojure for the many related design decisions to compound together. There's no magic solution to that (other than "just use Clojure" :). > And you can sometimes end up with a handful of these, so the 'glue code' > isn't quite so small. Also gen-class and definterface I find similarly > nice. > I find the glue code is actually small in practice (I've done a couple of real systems this way). This particular example is a little weird because it's just making a domain object, but generally you're writing the glue to provide a factory method for a facade. Below the facade, the Clojure code can make new objects just fine, so you're staying in Clojure for the rest of it. > I wonder if there's a path to improve what you're calling option #2. > Specifically the needing to AOT compile *everything*. It seems I should > only have to AOT/precompile the surface area that my Java consumer wants to > link with. At runtime perhaps there'd be some creative way to leave out the > AOT classes (which would just have been used for static-time compiling) and > when the JVM tries to load those classes some agent could interject and do > the dynamic Clojure loading? > Seems like you're taking the hardest path and trying to make it harder. I just don't see the point in running at this one, sorry. My experience is that the approach above works great and has none of these issues. > > > On Sunday, June 9, 2019 at 11:21:28 PM UTC-5, Alex Miller wrote: >> >> Looks like the title for this is backwards if I understand the intent, >> should be calling Clojure from Java, right? >> >> Java code is going to invoke methods on classes (that's all it knows how >> to do). You have several options: >> >> 1) Use the Clojure Java API to invoke the Clojure runtime - here you're >> leveraging the Clojure runtime's Java impl core to call into Clojure (Stu's >> example) >> 2) Use protocols, records, and/or genclass to actually produce Java >> classes (your example, I think). >> 3) Define your interface as a set of Java interfaces. Implement this >> interface in Clojure. You will need a small amount of glue code to provide >> the API impl (some kind of a factory to give you the impl of the Java >> interface - either written in Java or using #1). >> >> #1 is straightforward but tedious - it's really only worthwhile if you >> have a small amount of this code and hide the use of it. >> #2 has significant downsides - needing to compile everything, all methods >> are just going to take/return Java Objects, no javadoc on apis, etc. >> #3 has advantages in all those areas. You write your Java interface where >> you can best write it ... in Java (with Javadoc, and Java interfaces, and >> all the niceties Java users want). IDEs can autocomplete on the Java >> interfaces. You don't have to AOT - factories can reify or load protocol >> instances as needed as they return objects. You can even reload internal >> vars with a connected REPL without restarting the app. >> >> The last does take a bit of planning to set up - you need API code (in >> Java), and Clojure code that relies on that Java code. lein makes it pretty >> trivial to put those in one project and compile in that order. >> >> I pushed an example up here: >> >> https://github.com/puredanger/clojure-from-java >> >> It defines a Java interface (java/cfj/Event.java >> <https://github.com/puredanger/clojure-from-java/blob/master/java/cfj/Event.java>), >> a static helper to hook the Clojure (java/cfj/Support.java >> <https://github.com/puredanger/clojure-from-java/blob/master/java/cfj/Support.java>), >> which uses the Clojure Java API to load the Clojure instance in >> clj/core/cfj.clj >> <https://github.com/puredanger/clojure-from-java/blob/master/src/cfj/core.clj>. >> Just `lein uberjar` and you're done. Use the Support API from Java as any >> other Java class. No AOT required. >> >> >> >> On Sunday, June 9, 2019 at 9:53:56 PM UTC-5, eglue wrote: >>> >>> I've been stalking Clojure for a while but now doing it professionally >>> full-time (woo hoo!). >>> >>> I saw a Stuart Halloway tweet responding to someone who'd found it a >>> "soul-crushing, miserable experience." >>> >>> I had a similar miserable experience and figured it was just me, but am >>> now suspecting that's not the case. (Happy to be shown the light however.) >>> >>> Stuart H posted https://github.com/stuarthalloway/clojure-from-java >>> <https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fstuarthalloway%2Fclojure-from-java&sa=D&sntz=1&usg=AFQjCNFxHASS9MQ07WZ5St1GE9g1WckHFg> >>> to >>> demonstrate the ease of Java consuming Clojure, but I find it far more >>> important to be able to *compile* Java against Clojure interfaces not >>> invoke it dynamically from Java....because dynamic invocation from Java is >>> unwieldy to the point of it being a likely deal breaker for any Java shop. >>> Dynamically loading classes and invoking methods.... c'mon, no one's going >>> to ask their Java devs to do this. >>> >>> If Clojure doesn't have a good compile-time consumption story for Java >>> consumers, I think that's a loss. (Even if it is just providing better docs >>> or archetype/bootstrap examples in this regard.) Because otherwise Java >>> code bases around the world could be eaten away by (compiled) Clojure from >>> the inside out, giving Java dev teams enough time to be overtaken by the >>> miracle that is Clojure. >>> >>> Inspired by Stuart's example, I was successful in putting together a >>> quick build to achieve this ideal: >>> https://github.com/atdixon/clojure-from-java >>> <https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2Fatdixon%2Fclojure-from-java&sa=D&sntz=1&usg=AFQjCNG0DEccFtsm0Norw4jQ_qw-dezdZw> >>> >>> However, I have a few open questions: >>> >>> - when I tried to AOT only the public-facing clojure code that I needed >>> to compile against, I found out at runtime that this wasn't going to work. >>> Dynamic clojure code was loading my same types into DynamicClassLoader and >>> when my statically-compiled, root-class-loaded code was getting executed >>> the ClassCastExceptions of course were flying. So am I right to think that >>> you have to AOT everything in order to do this right? >>> - IDE support (for me, Cursive) "works" but is non-ideal; you have to >>> manually compile the dependee/Clojure jar, then Cursive will let you >>> execute Java code against the manually aot-compiled Clojure code >>> - AOT producing generics/generic types doens't seem to be part of any of >>> this... is this a lacuna in the Clojure AOT space, are there libs that can >>> help here? >>> >>> This story ^^, if made easier, seems to me would boost Clojure adoption >>> in Java/JVM shops. >>> >>> What am I missing? >>> >>> >>> -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to a topic in the > Google Groups "Clojure" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/clojure/aejqMwraPk8/unsubscribe. > To unsubscribe from this group and all its topics, send an email to > clojure+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/clojure/07753636-3414-481f-8836-763ca85675ce%40googlegroups.com > <https://groups.google.com/d/msgid/clojure/07753636-3414-481f-8836-763ca85675ce%40googlegroups.com?utm_medium=email&utm_source=footer> > . > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/clojure/CAOdgdgzCFd3oSSyq0VNOkDftdohgPhmKh1T8uTdCXxC0uhzKnw%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.