Fork me on GitHub
#clojure
<
2021-08-16
>
p-himik07:08:54

A question about gen-class. I'm trying to use Quil and I noticed that a huge amount of time is spent on... changing color of the lines. The sampling results are on the screenshot. And here's the relevant code:

(gen-class
 :name "quil.Applet"
 :implements [clojure.lang.IMeta]
 :extends processing.core.PApplet
 :state state
 :init quil-applet-init
 :constructors {[java.util.Map] []}
 :exposes-methods {keyTyped keyTypedParent
                   mouseDragged mouseDraggedParent
                   keyPressed keyPressedParent
                   mouseExited mouseExitedParent
                   mouseClicked mouseClickedParent
                   mouseEntered mouseEnteredParent
                   mouseMoved mouseMovedParent
                   keyReleased keyReleasedParent
                   mousePressed mousePressedParent
                   focusGained focusGainedParent
                   frameRate frameRateParent
                   mouseReleased mouseReleasedParent
                   focusLost focusLostParent
                   sketchFullScreen sketchFullScreenParent})

(defn -meta [this]
  (.state this))
So it seems to me that Clojure doesn't know that this above is an instance of quil.Applet. Is that observation correct? How should it be fixed?

kwladyka07:08:04

I am not sure but did you check reflections?

p-himik07:08:02

The pic shows that the reflection happens inside Quil itself, right in that -meta function - assuming I'm reading the sampling results correctly. Meaning, that Clojure doesn't assume that this is an instance of quil.Applet. I would like to confirm that.

kwladyka08:08:20

Try to add ^quil.Applet to make it strong type for this

kwladyka08:08:31

Like in the link with example

p-himik08:08:56

I do not control the source code of Quil. :) I'd have to learn how to build it first - and before going down that potentially complicated path, I'd like to confirm my guess.

reefersleep08:08:41

Changing the color of a line is IO. IO is generally the most expensive part of your code, right? I’ve no idea whether it’s unreasonably expensive in this case, though, or whether you’ve got other, similar IO that’s less expensive.

reefersleep08:08:32

I’ve been annoyed with rendering times as well while tinkering with quil, but I assume it’s par for the course when you’re used to iterating on mostly pure functions 🙂

p-himik08:08:14

How is it IO? It's just storing a constant to be used for the following rendering operations. You can see from the screenshot above that the vast majority of the time is spent in the reflection, not in some IO.

reefersleep08:08:01

Oh, sorry. I didn’t look into the details 😕 I thought you were talking specifically about changing the image.

reefersleep08:08:40

Pardon the noise!

kwladyka09:08:03

just check reflections. It will show you reflections issues also in dependencies.

p-himik09:08:05

It is inside gen-class. Which works only during AOT. So unless *warn-on-compile* was set when Quil was built, it won't show anything.

p-himik09:08:38

Unless, of course, I misunderstand how it all works. Either way, there are no warnings. But that Reflector call is still there.

kenny15:08:28

I'm curious why some reader tags throw when evaluated on their own. For example, take the #ordered/map tag (reader kv defined https://github.com/clj-commons/ordered/blob/12044526cdda3f0ff08176666210022397621997/src/data_readers.clj#L2, https://github.com/clj-commons/ordered/blob/12044526cdda3f0ff08176666210022397621997/src/flatland/ordered/map.clj#L149). If I eval the following code, I get an exception thrown (full stacktrace in thread).

(do #ordered/map ([:b 2] [:a 1] [:d 4]))
Syntax error (IllegalArgumentException) compiling fn* at (src/example.clj:6:3).
Unable to resolve classname: IPersistentMap
The map is read and the data reader fn is called as expected.
(def omap #ordered/map ([:b 2] [:a 1] [:d 4]))
=> #'example/omap
(type omap)
=> flatland.ordered.map.OrderedMap
It's only when evaling just the tagged literal that the ex is thrown. Any idea why this is happening?

kenny15:08:49

Full stacktrace

Syntax error compiling fn* at (/src/example.clj:6:3).
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7119)
java.lang.IllegalArgumentException: Unable to resolve classname: IPersistentMap
	at clojure.lang.Compiler$HostExpr.tagToClass(Compiler.java:1129)
	at clojure.lang.Compiler.tagClass(Compiler.java:8693)
	at clojure.lang.Compiler$ObjExpr.emitValue(Compiler.java:4810)
	at clojure.lang.Compiler$ObjExpr.emitConstants(Compiler.java:4938)
	at clojure.lang.Compiler$ObjExpr.compile(Compiler.java:4616)
	at clojure.lang.Compiler$FnExpr.parse(Compiler.java:4110)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7109)
	at clojure.lang.Compiler.analyze(Compiler.java:6793)
	at clojure.lang.Compiler.eval(Compiler.java:7178)
	at clojure.lang.Compiler.eval(Compiler.java:7171)
	at clojure.lang.Compiler.eval(Compiler.java:7136)
	at clojure.core$eval.invokeStatic(core.clj:3202)
	at clojure.core$eval.invoke(core.clj:3198)
	at nrepl.middleware.interruptible_eval$evaluate$fn__939.invoke(interruptible_eval.clj:91)
	at clojure.main$repl$read_eval_print__9110$fn__9113.invoke(main.clj:437)
	at clojure.main$repl$read_eval_print__9110.invoke(main.clj:437)
	at clojure.main$repl$fn__9119.invoke(main.clj:458)
	at clojure.main$repl.invokeStatic(main.clj:458)
	at clojure.main$repl.doInvoke(main.clj:368)
	at clojure.lang.RestFn.invoke(RestFn.java:1523)
	at nrepl.middleware.interruptible_eval$evaluate.invokeStatic(interruptible_eval.clj:84)
	at nrepl.middleware.interruptible_eval$evaluate.invoke(interruptible_eval.clj:56)
	at nrepl.middleware.interruptible_eval$interruptible_eval$fn__965$fn__969.invoke(interruptible_eval.clj:155)
	at clojure.lang.AFn.run(AFn.java:22)
	at nrepl.middleware.session$session_exec$main_loop__1067$fn__1071.invoke(session.clj:190)
	at nrepl.middleware.session$session_exec$main_loop__1067.invoke(session.clj:189)
	at clojure.lang.AFn.run(AFn.java:22)
	at java.base/java.lang.Thread.run(Thread.java:829)

Ed17:08:32

I'm not sure, but I think that in one scenario, the namespace context is being set correctly, and in the other it isn't? The OrderedType type has metadata with class information on it saying IPersistentMap, and in the form that doesn't throw the error it's resolving that to clojure.lang.* ? But I think it has something to do with it being emitted, because the code successfully runs.

Ed17:08:25

if you redefine the ordered-mapfunction like this:

(defn ordered-map
  "Return a map with the given keys and values, whose entries are
sorted in the order that keys are added. assoc'ing a key that is
already in an ordered map leaves its order unchanged. dissoc'ing a
key and then later assoc'ing it puts it at the end, as if it were
assoc'ed for the first time. Supports transient."
  ([] empty-ordered-map)
  ([coll]
   (prn '->> coll (into empty-ordered-map coll))
   (reduce #(assoc %1 (first %2) (second %2)) {} coll))
  ([k v & more]
     (apply assoc empty-ordered-map k v more)))
you get this output
flatland.ordered.map=> (do #ordered/map ([:b 2] [:a 1] [:d 4]))
->> ([:b 2] [:a 1] [:d 4]) #ordered/map ([:b 2] [:a 1] [:d 4])
->> ([:b 2] [:a 1] [:d 4]) #ordered/map ([:b 2] [:a 1] [:d 4])
{:b 2, :a 1, :d 4}

Ed17:08:40

(I'm also interested that it get's called twice ...)

Ed17:08:54

maybe if you define the OrderedMap with fully qualified tags the problem will go away???

Ed17:08:42

also, notice that you don't get the error if you eval from within the flatland.ordered.map namespace

Ed17:08:01

flatland.ordered.map=> #ordered/map ([:a 1] [:b 2])
#ordered/map ([:a 1] [:b 2])

Ed17:08:20

so, maybe just importing the correct classes will fix the problem?

kenny18:08:12

Interesting. That fix seems like it'd defeat the purpose of the tagged literal 🙂

Ed09:08:00

oh yeah ... I'm not saying it's not a bug ;)

Matsu15:08:19

I'm writing a blog post and want to showcase how Clojure is built from just a few "keywords", meaning that there's only a few built in definitions in the Clojure language, and the rest are from clojure/core. I remember Rich Hickey telling the exact number of those base keywords in some talk but can't find it anywhere. Anyone know what it's at the moment? I mean words like class interface void etc.

Matsu15:08:33

Cheers! So 24 🙂

Matsu15:08:41

That's amazing

noisesmith16:08:41

the source of truth: if it's interned to compiler state here, it is part of the compiler and not defined in clojure code https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L40

👀 3
noisesmith16:08:52

@U01ML3G5WT0 out of "Class, interface void etc." only Class

3
dominicm19:08:29

If I have this code:

(ns test)

(set! *warn-on-reflection* true)

(definterface Foo
  (methodA []))

(defn make-foo
  []
  (proxy [Foo] []
    (methodA [] 1)))

(defn method-a
  [^Foo foo]
  (.methodA foo))

(comment
  (method-a (make-foo)))
And then I re-evaluate the namespace, I get:
class test.proxy$java.lang.Object$Foo$cf6fbe2b cannot be cast to class test.Foo (test.proxy$java.lang.Object$Foo$cf6fbe2b is in unnamed module of loader clojure.lang.DynamicClassLoader @1aa836af; test.Foo is in unnamed module of loader clojure.lang.DynamicClassLoader @63c807fb)
Am I using definterface or type hinting wrong?

p-himik19:08:08

My guess is that you have a reference to the result of (make-foo) somewhere and pass it to some place that expects Foo. You reload test, you get new Foo, but the old reference remains, mentioning the old Foo. A pretty common problem.

p-himik19:08:32

Or is that method-a the only place where Foo is mentioned, apart from the proxy?

p-himik19:08:07

And you re-evaluate this namespace in its entirety, and without holding any references to method-a?

dominicm19:08:22

@U2FRKM4TW method-a is the only place it's referenced and I'm re-evaluating the namespace in its entirety 🙂

Alex Miller (Clojure team)19:08:40

is there some reason to use proxy here vs reify?

dominicm19:08:30

I'll try with reify.

Alex Miller (Clojure team)19:08:50

also I think the proxy method impl needs to take an additional first "this" argument

dominicm19:08:15

reify doesn't have this problem 😮 magic.

dominicm19:08:26

@U064X3EF3 I thought proxy was anaphoric?

dominicm19:08:18

Why doesn't reify have this problem? Is the problem with Proxy referencing the wrong Foo?

dominicm19:08:41

@U064X3EF3 Oh, I need proxy because I'm doing java.io.FilterInputStream and need to pass an InputStream.

hiredman19:08:03

When doing input steam stuff it can be nicer to implement a channel interface and use one of the channel to input stream adapters nio ships with

dominicm19:08:40

I'd prefer to stay with an InputStream as I expect my users have one (e.g. to pass it to cheshire)

hiredman19:08:31

The thing is, http://java.io is a mess of concrete inheritance and things like filterinputsream which is basically an abstract base class, and every interface has too many methods

hiredman19:08:03

Where java nio has some nice small interfaces

hiredman19:08:50

(sorry, http://java.io has no interfaces, every abstract base class has too many methods)