Fork me on GitHub
#clojure
<
2020-11-29
>
Burin05:11:48

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})

lilactown05:11:41

For the second you can skip the & []

lilactown05:11:24

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

p-himik05:11:28

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

p-himik05:11:59

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

Burin05:11:03

Very nice. Thanks for the link and ^

p-himik05:11:36

[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.

👍 6
valerauko08:11:48

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

nbardiuk09:11:05

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

nbardiuk09:11:38

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

nbardiuk09:11:37

195 overflows to -61

p-himik09:11:14

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

👍 3
valerauko10:11:44

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-himik11:11:31

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;

andy.fingerhut21:11:25

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

andy.fingerhut21:11:04

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

👍 3
valerauko08:12:04

I really wish things like byte-array would be smart and handle these cases for me 😞

Itay13:11:01

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 Sless14:11:05

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.

Itay14:11:03

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

Ben Sless14:11:18

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.

Itay14:11:33

Yup 🙂

lilactown21:11:52

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

lilactown21:11:51

looks like no...

andy.fingerhut21:11:28

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.

lilactown21:11:59

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

lilactown21:11:27

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

lilactown21:11:32

I’m used to CLJS where IFn is a protocol