clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
stuartrexking 2020-11-30T02:20:24.314600Z

How do a call a specific java method (which is overloaded by type, rather than number of params)?

2020-11-30T02:28:59.314700Z

Often giving a type hint on at least one of the parameters that has a distinct type from the other java method signatures should do it. Might require multiple type hints if one isn't enough to make the signature unique.

2020-11-30T02:29:16.314900Z

e.g. (.myMethod instance1 ^long x y z w)

2020-11-30T02:30:00.315100Z

that would be if the first parameter x is type long in the Java method signature. Can also type hint any or all of the other parameters.

stuartrexking 2020-11-30T02:41:00.315500Z

Like this

(.setNonStrokingColor my-instance ^PDColor colour)

stuartrexking 2020-11-30T02:41:25.315700Z

But that method is overloaded with a Color type arg as well.

2020-11-30T02:42:06.315900Z

Have you added (set! **warn-on-reflection** true) near the beginning of the source file where you make that call? e.g. after the ns form.

stuartrexking 2020-11-30T02:42:33.316100Z

No!

2020-11-30T02:42:59.316300Z

That can help detect when the Clojure compiler has not successfully resolved the method down to only one choice at Clojure compile time.

2020-11-30T02:43:24.316500Z

Adding it will not help the compiler pick one of the methods, but it will quickly tell you if it hasn't.

2020-11-30T02:44:48.316700Z

Any reflection warnings you see when that is present in a file imply that the Clojure compiler will perform run-time Java reflection on each call it warns about, which can be quite slow relative to when those warnings are not there. I wouldn't worry about such warnings in code that is run once or a few times, but if it is in a hot code path you want to be fast, best to eliminate it.

2020-11-30T02:45:32.316900Z

When you say "it doesn't work for me", what do you see happening in your call that isn't working?

stuartrexking 2020-11-30T02:45:34.317100Z

And just to confirm, type hinting isn’t guaranteed to work?

stuartrexking 2020-11-30T02:45:41.317300Z

I get a class cast exception.

2020-11-30T02:46:25.317500Z

Are you certain that the type of the argument is actually PDColor, e.g. you have done a debug print or something similar just before that method call to show the class of the argument?

2020-11-30T02:47:25.317700Z

maybe something like (if (not (instance? PDColor foo)) (println "(class foo)=" (class foo)))

stuartrexking 2020-11-30T02:47:37.317900Z

I have. I’ll double check that.

2020-11-30T02:47:58.318100Z

It might be that 99.9% of the time it is the class you expect, but it only takes one that isn't ...

stuartrexking 2020-11-30T02:56:47.318300Z

Here is what I know: 1. Class is definitely the right type 2. Type hinting makes no difference to which function is being called. I’ve even put a debug breakpoint in the function that should be called and it’s not being called.

stuartrexking 2020-11-30T02:57:01.318500Z

I might have to use a different signature.

2020-11-30T02:57:31.319Z

Do you know which one is being called?

stuartrexking 2020-11-30T02:58:18.319800Z

This is the error

class org.apache.pdfbox.pdmodel.graphics.color.PDColor cannot be cast to class [F (org.apache.pdfbox.pdmodel.graphics.color.PDColor is in unnamed module of loader 'app'; [F is in module java.base of loader 'bootstrap')

2020-11-30T02:58:21.320100Z

Or none of them are, because the first time you try you get an exception?

stuartrexking 2020-11-30T02:58:35.320300Z

I don’t think any are being called.

2020-11-30T02:59:10.320800Z

Do you have a stack trace with that exception?

stuartrexking 2020-11-30T02:59:26.321Z

No

stuartrexking 2020-11-30T02:59:48.321300Z

Can I generate one? Nothing coming out in the REPL

stuartrexking 2020-11-30T03:00:07.322Z

let me try catch it

2020-11-30T03:00:34.323Z

A stack trace could tell you if the error is occurring in your code, at the call you are interested in now, or somewhere within the depths of the Java library

2020-11-30T03:01:01.323800Z

*e is the value of the last exception in a REPL session

2020-11-30T03:01:18.324400Z

You can show it by evaluating that form

2020-11-30T03:07:51.324800Z

The most recent calls appear first in the stack trace. The first few are inside of Clojure's implementation of run-time reflection it appears

2020-11-30T03:08:24.325Z

The first stack frame that is not inside of Clojure is this one: [ventures.fierce.pdfbox.appearance_content_stream$non_stroking_color_GT_ invokeStatic "appearance_content_stream.clj" 39]

2020-11-30T03:08:36.325200Z

The 39 should be a line number in the file appearance_content_stream.clj

2020-11-30T03:10:17.325400Z

The exception message is this: "Cannot cast org.apache.pdfbox.pdmodel.graphics.color.PDColor to [F"

2020-11-30T03:10:56.325600Z

complaining that it has a value of type PDColor, and it cannot cast it to type [F, where [ denotes a Java array of the next thing, and F is a 1-letter abbreviation for one of the Java primitive types, I think float IIRC

2020-11-30T03:11:37.325800Z

An array of primitive floats is the type of the only parameter of one of the other 1-parameter signatures of that method setNonStrokingColor, I see.

2020-11-30T03:12:46.326Z

If you added that set! statement to warn on reflection, do you get a warning on this line 39?

2020-11-30T03:13:43.326200Z

Out of curiosity, do you have the dependency you added to your Leiningen project.clj file or deps.edn file for this apache library?

2020-11-30T03:14:01.326400Z

I might try to create a REPL locally on my machine and make a call to that method to see if I get the same behavior.

stuartrexking 2020-11-30T03:16:25.326600Z

I’m using deps.edn

stuartrexking 2020-11-30T03:16:53.326800Z

I don’t get a warning when I have the type on the instance (not the param)

stuartrexking 2020-11-30T03:17:13.327Z

Would you like me to share the repo with you?

2020-11-30T03:19:35.327200Z

If it is public, or you otherwise don't mind.

stuartrexking 2020-11-30T03:23:46.327400Z

https://github.com/stuartrexking/pdfboxing

stuartrexking 2020-11-30T03:24:24.327700Z

If you want to trigger it run the code in the comment block at the end of the core ns.

stuartrexking 2020-11-30T03:24:43.327900Z

I’m mid refactor so the output is likely to be broken or not output at all.

stuartrexking 2020-11-30T03:25:01.328100Z

I’m trying to isolate all the icky java interop.

stuartrexking 2020-11-30T03:25:20.328300Z

If you have any tips on how to improve the code please let me know.

stuartrexking 2020-11-30T03:25:24.328500Z

Thanks for your help.

2020-11-30T03:27:44.328700Z

It may not matter, but what OS, JDK version (e.g. 8, 11, etc.), and Clojure version are you using?

stuartrexking 2020-11-30T03:34:03.328900Z

Java

openjdk 11.0.2 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)

stuartrexking 2020-11-30T03:34:51.329100Z

Clojure Version: 1.10.1.727

stuartrexking 2020-11-30T03:35:23.329300Z

MacOS 11.01

2020-11-30T03:36:49.329500Z

I am running macOS 10.14.6, AdoptOpenJDK 11 something, and Clojure 1.10.1, and am seeing a similar excpetion

2020-11-30T03:47:05.329700Z

This is still weird to me so far. Clojure is using its run-time reflection techniques for this call, which is slower, but usually succeeds in finding the correct method. In this case, it is only finding that the class has 1 non-static method with 1 parameter, but clearly there are more.

2020-11-30T03:47:15.329900Z

Or at least the documentation says there are more ...

stuartrexking 2020-11-30T03:50:44.330100Z

If I grab the source of that version of the jar I can see the methods in the abstract parent class.

stuartrexking 2020-11-30T03:51:15.330300Z

And if I use the IntelliJ decompiler on the compiled code I can see them as well.

2020-11-30T03:52:45.330500Z

The method you want has the 'bridge' and 'synthetic' flags set in that class. The one with the float[] type parameter has neither of those 'bridge' nor 'synthetic' flags.

2020-11-30T03:53:13.330700Z

The Clojure reflector looks like it might use methods without the 'bridge' flag in preference to those that do.

2020-11-30T03:53:43.330900Z

I don't know why it does that

2020-11-30T03:55:08.331100Z

It is also odd to me that the reflection warnings do not show up at all for the method call that is causing this exception.

2020-11-30T03:55:41.331300Z

That would suggest to me that the Clojure compiler should have found what it thinks is a matching method at compile time, and not try to look for one at run time.

2020-11-30T03:56:13.331500Z

But if the compile time looking for a method has this same behavior of preferring non-bridge methods over bridge methods, perhaps that is part of the cause, too.

2020-11-30T03:59:00.331700Z

• What abstract parent class? According to the Javadoc page you sent me a link to, the class PDAppearanceContentStream extends class Object directly (or I misunderstand that JavaDoc page, perhaps)

stuartrexking 2020-11-30T04:00:02.331900Z

I might be confusing myself. Let me through it again…

stuartrexking 2020-11-30T04:02:20.332100Z

The Javadoc is incorrect. The class clearly has an abstract parent https://github.com/apache/pdfbox/blob/trunk/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/PDAppearanceContentStream.java

stuartrexking 2020-11-30T04:02:29.332400Z

Something not right with this lib.

stuartrexking 2020-11-30T04:02:54.332600Z

Or that version of it’s javadoc.

stuartrexking 2020-11-30T04:07:35.332800Z

I think that Javadoc isn’t correct.

2020-11-30T04:08:53.333Z

Agreed.

2020-11-30T04:10:26.333200Z

I just sent a message to the #clojure-dev channel describing this situation generally.

2020-11-30T04:10:45.333400Z

I can try adding a link there to this discussion thead, in case someone sees it and is interested in looking further.

stuartrexking 2020-11-30T04:10:50.333600Z

Ok thanks.

stuartrexking 2020-11-30T04:11:04.333900Z

I appreciate your help

2020-11-30T04:11:17.334100Z

Sure, no problem. Not there yet, obviously, but some clues.

2020-11-30T04:25:21.334300Z

There may be a completely-in-Clojure way to handle this, but if so, I don't yet know what that is.

2020-11-30T04:25:59.334500Z

If you are willing to go the length of writing a little bit of Java code, you could write that method call in Java, and it will probably be able to find the right method.

2020-11-30T04:27:16.334700Z

Then call that Java method you write from Clojure via Java interop (as long as you don't write several methods with same name & # of parameters that leads to this same issue in your Java code, of course)

kenj 2020-11-30T07:12:55.335400Z

is it common to write in spec.test checks into unit tests, or are they normally just run by hand from the REPL?

seancorfield 2020-11-30T16:59:05.338300Z

@risinglight I wouldn't say it was "common" to write such check's into unit tests but I think people do it because they have no better guidance on how/when to run generative tests right now.

seancorfield 2020-11-30T17:00:03.339300Z

I have some "small" check's in unit tests but mostly I put them in RCFs and run them manually from time to time when the code has changed significantly.

seancorfield 2020-11-30T17:00:47.340100Z

You could always use test metadata tagging to selectively run "unit" tests and "integration" tests separately (although I don't know how common that is either).

kenj 2020-11-30T17:04:14.341100Z

Thanks, I’ve definitely been struggling a bit with the lack of guidance part. Same with when/how exactly to utilize instrument .

2020-11-30T17:07:01.341600Z

> RCFs What does this stand for

Heather 2020-11-30T17:08:20.341800Z

do you have recommendations on where to get guidance on generative tests? We’re looking into them for our team.

seancorfield 2020-11-30T17:38:36.342Z

I really wish I did, for our team too.

seancorfield 2020-11-30T17:40:16.342200Z

The practice we've adopted is to have a few generative tests in our "unit test" suite but with fairly low iterations so they don't take long (since unit tests are meant to be fast), and then to have additional generative tests in comment forms that we run manually from time to time. Not a great solution, to be honest.

seancorfield 2020-11-30T17:41:47.342400Z

Rich Comment Forms. Using comment to have blocks of dev/test code in your source files. Stu Halloway coined the name, because it's an approach that Rich Hickey uses quite a bit. I've been doing it for quite a while before I heard that from Stu, so it's kind of nice validation of the technique to know Rich (and others) do this too.

seancorfield 2020-11-30T17:42:52.343800Z

@risinglight What I tend to do with instrument is to add it to the top of my test files, to instrument functions in the source namespace corresponding to the test namespace. You can see that in action in the tests for next.jdbc for example.

👍 1
seancorfield 2020-11-30T17:43:45.344700Z

I'll also enable it manually sometimes during dev, especially when I'm exploring parts of our code base that I didn't write.

2020-11-30T17:45:31.344800Z

Ah, got it, thanks!

ordnungswidrig 2020-11-30T19:28:25.346200Z

Hi! Who happens to have a link to a list of clojure(script) static website / blog generators? I have a octopress installation which is littly rusty now and I want to replace it with something I do understand.

borkdude 2020-11-30T19:42:48.347200Z

@ordnungswidrig #cryogen seems to be a well supported Clojure static site generator. Some people have also used babashka + bootleg (e.g. https://github.com/lambdaisland/gaiwan_co#tech-stack) but that's a more do it yourself solution.

👍 1
borkdude 2020-11-30T19:58:43.348100Z

@ordnungswidrig https://www.clojure-toolbox.com/ also has a section on this

👍 1