Fork me on GitHub
#clojure
<
2020-06-07
>
dominicm08:06:00

With this asciidoc class https://www.javadoc.io/doc/org.asciidoctor/asciidoctorj/latest/org/asciidoctor/jruby/ast/impl/DescriptionListEntryImpl.html if I extend both DescriptionListEntry and StructuralNode to a protocol, which takes priority? I seem to be getting variable behavior where sometimes it's passed to the StructuralNode and sometimes to the DescriptionListEntry extension. (Both protocols are extended in the same extend-protocol block, so there's no parallel out-of-order loading or anything like that)

dominicm09:06:42

#{org.asciidoctor.ast.DescriptionListEntry org.asciidoctor.jruby.ast.impl.StructuralNodeImpl} #{org.asciidoctor.jruby.ast.impl.StructuralNodeImpl org.asciidoctor.ast.DescriptionListEntry} ^^ these are taken from (parents org.asciidoctor.jruby.ast.impl.DescriptionListEntryImpl) it would seem that JRuby loads them in a variable order (this was produced by just running it over & over until I got a different result). So I'm guessing that occasionally Clojure is going to run through them differently, first going through the StructuralNodeImpl. Is that right? Shouldn't it use the immediate parent first, rather than traversing through StructuralNodeImpl to get to StructuralNode?

dominicm10:06:09

Looks like clojure specifies this on https://clojure.org/reference/protocols > You can implement a protocol on an interface > but opens the door to incidental multiple inheritance of implementation > since a class can inherit from more than one interface, both of which implement the protocol > if one interface is derived from the other, the more derived is used, else which one is used is unspecified. I guess this falls into "Unspecified"

Alex Miller (Clojure team)13:06:21

One clever trick for this case is to check the actual concrete type at runtime and dynamically install a new extension for the concrete type that goes to the one you want

Alex Miller (Clojure team)13:06:34

So after the first call you get fast routing

dominicm16:06:08

Something like this?

(when (try (Class/forName "org.asciidoctor.jruby.ast.impl.DescriptionListEntryImpl")
           (catch Throwable _ nil))
  (extend-protocol IChildren
    (Class/forName "org.asciidoctor.jruby.ast.impl.DescriptionListEntryImpl")
    (-children [this]
      (conj
        (seq (.getTerms this))
        (.getDescription this)))))
(Inspired by when-class in clojure)

Alex Miller (Clojure team)16:06:15

not sure I understand why you're checking if the class exists as the condition

Alex Miller (Clojure team)16:06:47

I would expect something that looks at an impl and decides which of the possible extensions to do

Alex Miller (Clojure team)16:06:29

I'm talking about a condition inside an extension

Alex Miller (Clojure team)16:06:21

I have an example of this in ch 1 of Clojure Applied (really more about protocols on protocols, but it's the same technique)

dominicm16:06:20

Oh, hmm. I maybe misunderstood you. I'm checking if the class exists because it's an internal implementation detail of the library I'm using and it might go away.

Alex Miller (Clojure team)16:06:02

yeah, you misunderstood - you can do this dynamically without caring about the actual class

Alex Miller (Clojure team)16:06:51

(extend-protocol Foo
  SomeInterface
  (some-method [o]
    (if (..some-condition..)
      (do (extend-protocol Foo
            (class o)  ;; whatever impl, don't care, just detect at runtime
            (some-method [o]
              (some-method-impl o))
          (some-method o))  ;; re-invoke but now we've installed a handler
      ...)))

Alex Miller (Clojure team)16:06:12

instead of SomeInterface there, this could be Object too - the catch-all default for protocols. basically if you invoke the protocol method on some unknown type, detect that, use whatever tools you need to determine how to handle it, dynamically install a handler for the concrete class, and re-invoke

dominicm16:06:54

Ah, I was going to say that it would be slow, but on Object that's not a problem (for me)

Alex Miller (Clojure team)17:06:03

it's only slow the first time

Alex Miller (Clojure team)17:06:17

after that, the concrete class is installed and you never hit the conditional logic again

dominicm17:06:18

It would slow down a particular implementation if the SomeInterface had an actual implementation tied to it, unless you always extend it to something concrete.

Alex Miller (Clojure team)17:06:00

again, there are only so many concrete impls - after you've seen each of them once, you'll never hit the conditional logic

Alex Miller (Clojure team)17:06:50

at the very first conj, Chris Houser was doing a talk about protocols and someone asked a question like this and Rich suggested this technique from the audience, which is where I learned it. have used it several times.

Alex Miller (Clojure team)17:06:00

you do have to be careful of anonymous impls or reify's etc as those are one-off class instances

Alex Miller (Clojure team)17:06:42

but generally I've used this when I control enough of the use and domain to assess that

ncouturier08:06:05

Hi Could someone point me to a data management oriented, non web, project ? Many thanks.

dominicm09:06:42

#{org.asciidoctor.ast.DescriptionListEntry org.asciidoctor.jruby.ast.impl.StructuralNodeImpl} #{org.asciidoctor.jruby.ast.impl.StructuralNodeImpl org.asciidoctor.ast.DescriptionListEntry} ^^ these are taken from (parents org.asciidoctor.jruby.ast.impl.DescriptionListEntryImpl) it would seem that JRuby loads them in a variable order (this was produced by just running it over & over until I got a different result). So I'm guessing that occasionally Clojure is going to run through them differently, first going through the StructuralNodeImpl. Is that right? Shouldn't it use the immediate parent first, rather than traversing through StructuralNodeImpl to get to StructuralNode?

tio09:06:35

Quick q: what is the equivalent of the ! symbol in Clojure?

tio09:06:31

Perfect; thank you!

4
tio09:06:49

You put this in front of variables if you want the truthy or falsey value.

tio09:06:51

Thank you.

ludociel13:06:17

quick question. so I've written a REST endpoint using ring/jetty, takes a file and sends back a json. (No static files) We are planning to use it as a micro-service. I'm new to java, clojure, ring, jetty and all. The question is, Should the file go out as .jar or .war? .jar works fine as it is inside a docker container, but is it better if I make it a .war? If so how?

dharrigan14:06:27

without knowing anything about your problem domain, a jar is fine.

dharrigan14:06:07

A war is only required if you're deploying to an existing web container (i.e., jetty, tomcat etc...)

dharrigan14:06:24

As Ring also includes jetty (as a dependency), then a war isn't required.

kimim14:06:36

You can lein ring uberjar, then CMD java -jar abc-standalone.jar in dockerfile

ludociel16:06:34

oh then I'll settle with .jar then. Thanks

Drew Verlee16:06:37

Where do you go for your Java related news? It seems reasonable given clojure is hosted to pay attention to the ecosystem, but probably just the best hits. Like is there a conference that I tends to be good?

jumar07:06:31

I don't watch "Java news" closely but I somewhat follow DZone and InfoQ updates which are often about Java ecosystem. I also subscribed to Java/JVM tag on Stackoverflow and there's the Java Specialists slack and related newsletter. Mechnical Sympathy is a also a great mailing lists which is often about JVM internals.

👍 4
jumar07:06:15

If you want to dig deeper than it might be fun to follow JVM experts - some of my favorite: • Gil Tene • Brian Goetz • Cliff Click • Andrei Pangin • Martin Thompson

Drew Verlee11:06:39

Thanks jumar I'll take a look.

ludociel16:06:09

Thanks for helping with my questions everyone! from #beginners and here here is the really simple project [rest API which takes a document and returns mime-type, ext and text] using ring and tika. this is my first clojure project. I made it this weekend. https://github.com/greed2411/tokyo ik it might be doxing myself, but please have a look at it and give me suggestions if possible. thanks for helping me out throughout the project

adam20:06:28

Any idea how I can access individual fields of a submitted HTML form when using Liberator? The only bit I was able to find is this: http://clojure-liberator.github.io/liberator/tutorial/post-et-al.html But (slurp (get-in ctx [:request :body])) is giving me "email=hello%", while I want a way to get

ordnungswidrig20:06:31

You can use standard ring middleware to parse the POST parameters. These will be available in :params or :form-params. See https://ring-clojure.github.io/ring/ring.middleware.params.html (Feel free to continue on #liberator)

adam20:06:09

Thanks, got it. Will try that now.