clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
agilecreativity 2020-11-29T05:01:48.291100Z

quick question: Is there any advantage/disadvantage of define your function like:

(defn some-fn [a & {:keys [b c]}]
  (list a b c))
;; (some-fn 1 :b 2 :c 3)
;; vs

(defn some-fn [a & [{:keys [b c]}]
  (list a b c))
;; (some-fn 1 {:b 2 :c 3})

lilactown 2020-11-29T05:08:41.291600Z

For the second you can skip the & []

lilactown 2020-11-29T05:09:24.292800Z

(defn some-fn [a {:keys [a b]}]

p-himik 2020-11-29T05:09:28.293Z

Not really. It would make it impossible to pass one arg to the fn.

p-himik 2020-11-29T05:09:38.293300Z

Some musings here: https://stuartsierra.com/2015/06/01/clojure-donts-optional-arguments-with-varargs

๐Ÿ‘ 2
lilactown 2020-11-29T05:09:41.293600Z

Ah right

p-himik 2020-11-29T05:09:59.294100Z

It's better to create two separate arities, [a] and [a {:keys [b c]}].

agilecreativity 2020-11-29T05:11:03.295300Z

Very nice. Thanks for the link and ^

p-himik 2020-11-29T05:11:36.296Z

[a & {:keys [b c]}] is not that great. Yes, it enables the call sites to omit { and }. But it also makes it much harder to compose arguments. And it's error-prone - you can make a mistake and see a cryptic error message only in run time.

๐Ÿ‘ 2
valerauko 2020-11-29T08:49:48.297800Z

Can someone explain this behavior to me? I'd expect the two to result in the same string

user=> (Integer/toBinaryString 0xc3)
"11000011"
user=> (Integer/toBinaryString (byte 0xc3))
"11111111111111111111111111000011"
I figure it's to do with signed vs unsigned but it's weird

valerauko 2020-12-01T08:55:04.406200Z

I really wish things like byte-array would be smart and handle these cases for me ๐Ÿ˜ž

nbardiuk 2020-11-29T09:05:05.297900Z

On jdk11 it throws exception "Value out of range for byte: 195". I guess your runtime overflows

nbardiuk 2020-11-29T09:06:38.298100Z

I get the same binary with (Integer/toBinaryString (unchecked-byte 0xc3))

nbardiuk 2020-11-29T09:07:37.298300Z

195 overflows to -61

p-himik 2020-11-29T09:17:14.298500Z

It may depend on the relevant setting instead of the runtime:

user=> (byte 0xc3)
Execution error (IllegalArgumentException) at user/eval3 (REPL:1).
Value out of range for byte: 195
user=> (set! *unchecked-math* true)
true
user=> (byte 0xc3)
-61

๐Ÿ‘ 1
valerauko 2020-11-29T10:53:44.298800Z

Yeah that part is implementation. The base issue is why I'm told that 0xc3 which is obviously a valid single-byte value is "out of range for byte"

p-himik 2020-11-29T11:31:31.299Z

Because byte is signed:

/**
     * A constant holding the minimum value a {@code byte} can
     * have, -2<sup>7</sup>.
     */
    public static final byte   MIN_VALUE = -128;

    /**
     * A constant holding the maximum value a {@code byte} can
     * have, 2<sup>7</sup>-1.
     */
    public static final byte   MAX_VALUE = 127;

Itay 2020-11-29T13:44:01.308300Z

Java inter-op performance question. I have this Java interface (which I cannot change) that goes like:

public interface Span {
    Span setTag(String k, String v);

    Span setTag(String k, boolean v);

    Span setTag(String k, Number v);
}
And on the other hand a Clojure function that does something like (but not really):
(defn set-tag [^Span s k v]
      (.setTag s k v))
When I ran profiling I saw that this line creates an insane amount of reflection overhead (donโ€™t have the output handy ATM). The question is - is there a way to avoid reflection here that does not require changing the code calling set-tag? I can do something like:
(cond (string? v) (.setTag s k ^String v) ...)
But would that really be better? I understand that I may just have to try some things out and benchmark, but was wandering if there is some obvious solution for this problem that I am missing. Thanks!

Ben Sless 2020-11-29T14:20:05.308400Z

Dispatching with cond and instance checks won't introduce lots of overhead. In terms of performance, especially compared to reflection, it is immensely preferable. Another option is having three functions, such as set-string-tag, set-boolean-tag set-numerical-tag. More verbose, but will be clear what's expected in the code and will avoid instance checks. Anyway, to avoid reflection you'll need to type hint the v argument.

Itay 2020-11-29T14:25:03.308600Z

Thanks, this matches what I thought. What I fail to understand is why the compiler/runtime canโ€™t do this instead of meโ€ฆ

Ben Sless 2020-11-29T14:28:18.308800Z

For good or bad, the compiler is simple and predictable. It doesn't know what type to dispatch to at runtime, so it has to find all available methods then find the one whose arguments types match the provided arguments. This is further complicated by the fact that with inheritance you can't just match types, but all the types implementing an interface or inheriting another type. That's what the runtime does, although it could be theoretically possible to generate such dispatch code for simple cases.

Itay 2020-11-29T14:30:33.309Z

Yup ๐Ÿ™‚

lilactown 2020-11-29T21:16:52.309800Z

is there a way for me to extend IFn to an existing Java type?

lilactown 2020-11-29T21:17:51.310Z

looks like no...

2020-11-29T21:45:25.310100Z

Yeah, it can be a bit annoying that Java has no option for unsigned integer types. You can of course easily create a little function like (defn unsigned-byte-to-signed-byte [x] (if (>= x 128) (- x 256) x)) for converting unsigned to signed

2020-11-29T21:46:04.310300Z

That particular conversion function leaves the most significant bit of the byte unchanged.

๐Ÿ‘ 1
2020-11-29T21:48:28.310500Z

I believe that the answer "no" is correct for existing Java classes -- they cannot be extended to any new interfaces not in their original definition. That is one of the things that makes Clojure's defprotocol and defmulti more flexible.

lilactown 2020-11-29T21:51:59.311800Z

Right, I think the issue here is that IFn is an interface so I canโ€™t extend it to an existing type or class

lilactown 2020-11-29T21:52:27.312600Z

If IFn was a protocol, I could extend the class with it

lilactown 2020-11-29T21:53:32.313300Z

Iโ€™m used to CLJS where IFn is a protocol