Fork me on GitHub
#clojure
<
2024-06-30
>
dliotev15:06:03

Question about integers in Clojure annotations: I am trying to use a Java CLI library (https://picocli.info/) from Clojure. This library uses annotations on Java classes and methods, so I pass to it deftypes with annotated methods. And it mostly works so far, but in some cases it requires using literal integers in annotations, and it seems there is no way to do this in Clojure. The code I am trying is something like this:

(deftype SomeType []
  (^{Retention RetentionPolicy/RUNTIME
     SomeAnnotationClass {:order 1} }
   someMethod [_]...)) 
The :order 1 part causes an exception: Incorrectly typed data found for annotation element ... (Found data of type java.lang.Long[1]) . This happens because the java library expects that :order would have a java.lang.Integer value, but Clojure creates a java.lang.Long. If I try (int 1) instead of 1, I get another exception (`class clojure.lang.Var cannot be cast to class java.lang.Class` ). I also tried to def a var with an int value and use it in the annotation, but it does not work either. I wish there was a way to specify a literal integer in Clojure (like http://1.MNM for BigDecimal, or 1N for BigInt) - that would have solved the problem. Does anyone have an idea if there is a way around this limitation?

Alex Miller (Clojure team)16:06:00

Off the top of my head, don’t know if there is any way around this. Would welcome a problem report on http://ask.clojure.org

wikipunk16:06:49

does the read time constructor #java.lang.Integer [1] work? ie

(deftype SomeType []
  (^{Retention RetentionPolicy/RUNTIME
     SomeAnnotationClass {:order #java.lang.Integer[1]} }
   someMethod [_]...)) 

👍 1
dliotev16:06:43

@U23DRD8FN - #java.lang.Integer[1] does work, thanks! @U064X3EF3 - Thanks for the suggestion to create a problem report on http://ask.clojure.com: I would still like to create one, even though we already have a solution, because for example 1I (if we had 'I' as a literal int syntax) looks more natural (to me) than #java.lang.Integer[1] .

🎉 1
Alex Miller (Clojure team)17:06:19

Just report the problem, that is the most useful thing. There are lots of potential solutions, that one is not something we’re going to do

👍 1
frozenlock18:06:46

Is there a convention for naming the result of a predicate? (To differentiate from the predicate itself.)

(let [number?? (number? "abc")] ...)

seancorfield18:06:43

No. Some people use ? on local names but others believe ? should really be reserved for the predicate function itself.

seancorfield18:06:55

I would generally try to pick something descriptive for the local name, that expresses the domain well -- hard to suggest without real world code.

frozenlock19:06:56

Right, naming things is hard 😉 Thank you for your answer

didibus20:06:21

Some things I've seen: is/has prefix: is-number? (number? "abc") drop ?: number (number? "abc") flag suffix: number-flag (number? "abc") And one I like that's kind of what Sean mentioned I believe is to combine the predicate and the predicated For example, you probably have something like

(defn wtv
  [arg1 arg2 ...]
  (let [arg2-number? (number? arg2)]
    ..))
And sometimes I combine this with the prefix and do is-arg2-a-number? or something like that. But you see here the variable name reflects the predicate-predicated combination. What is being checked against which predicate.

✔️ 1
cjohansen05:07:33

The best solution is to not name it at all - do you really need to let this value, or can it be inlined?

1
p-himik08:07:25

I'm with Sean on this one - most of the time, it's not the distinction "a number/not a number" (or any other) that matters but rather the subsequent logic. Why the distinction is of any importance. Just as an example, suppose you have a value that represents an IPv4 address. If it's a string, it's probably x.x.x.x, but it can also be an integer. In that case, I'd write it as

(let [int-ip? (number? ip)}
  (cond-> ip int-ip? (parse-int-ip)))
Of course, in this trivial case a binding is completely unnecessary. But in more complicated cases it's better to have it. However, it doesn't really solve the problem of naming when the predicate is already named according to the subsequent logic. There, you have to get creative. :) One other approach I've seen quite a few times is to add ' at the end or the start of a name to make it "the same but different".