Fork me on GitHub
#clojure
<
2019-02-07
>
fabrao01:02:46

Hello all, how do I handle with java.util.Collections? Is there any way to convert it to Clojure collection?

andy.fingerhut01:02:48

If you have an arbitrarily nested java.util.Collections object, then you would need some kind of function that recursively 'walks' through it, converting each thing into a corresponding Clojure type. If you have a java.util.Set, and you know its elements are things like integers, strings, etc., then you should be able to simply call (set my-java-set), which will return a Clojure set.

andy.fingerhut01:02:04

I would bet that someone has written the recursive version of that for many kinds of java.util.Collections, but I don't know what library it might exist in.

fabrao01:02:25

It´s kind of Enumeration<String>

andy.fingerhut01:02:59

If you know it contains String elements, then (set my-java-collection) should do the job just fine.

andy.fingerhut01:02:06

unless you want a different kind of result, e.g. if you want a vector, then (vec my-java-collection)

fabrao01:02:40

look the solution -> (java.util.Collections/list my-java-collection)

fabrao01:02:45

it response as an array in Clojure

fabrao01:02:56

thank you for the help

andy.fingerhut01:02:44

You may want to assign the return value of that to a variable or let-bound symbol, and print out the value of (type val) to see if it really is a Clojure vector, if you truly need an immutable Clojure vector, and not a Java mutable type. Java mutable types can be printed at a REPL in a way that looks the same as Clojure collections.

andy.fingerhut01:02:04

user=> (java.util.ArrayList. [1 2 3])
[1 2 3]
user=> (type (java.util.ArrayList. [1 2 3]))
java.util.ArrayList

fabrao01:02:48

I see you point

andy.fingerhut01:02:10

java.util.Collections/list can convert the Enumeration type to a Java list, and from there you can call Clojure's set or vec on it to create an immutable value.

andy.fingerhut01:02:05

I was also just reminded while searching through the Clojure source code for "Enumeration" of the function enumeration-seq, which takes a Java Enumeration, and returns a Clojure sequence.

andy.fingerhut01:02:51

Using that instead of java.util.Collections/list might be a little more efficient, since it might not allocate as much memory

fabrao02:02:10

It worked with enumeration-seq too. That helped me a lot. Many thanks

orestis09:02:22

Some code golf — is there something that can simplify this a little bit?

(let [results [{:_id 1} {:_id 2} {:_id 5}]
        lookup {1 "a", 2 "b", 3 "c"}]
    (map #(assoc % :val (get lookup (:_id %) "default")) results))
;; => ({:_id 1, :val "a"} {:_id 2, :val "b"} {:_id 5, :val "default"})

erwinrooijakkers13:02:12

If you didn’t need a default you could have used

cljs.user=> (clojure.walk/postwalk-replace lookup results)
[{:_id "a"} {:_id "b"} {:_id 5}]

erwinrooijakkers13:02:18

Oh wait you could not 😉

erwinrooijakkers13:02:29

You also add :val

erwinrooijakkers13:02:58

To me it looks perfectly idiomatic as is

erwinrooijakkers13:02:41

Can also use for:

(for [{:keys [_id] :as e} results
      :let [val (get lookup _id "default")]]
  (assoc e :val val))

;; => ({:_id 1, :val "a"} {:_id 2, :val "b"} {:_id 5, :val "default"})

javi15:02:28

I have posted the same question in the cljs channel, but am also posting here as the behavior is the same in clj. I am exploring extend-via-metadata and I am getting this behavior:

(defprotocol Component
  :extend-via-metadata true
  (start [component]))

(def component (with-meta {:name "db"} {`start (constantly "started")
                                        `clojure.core.protocols/datafy (fn [x] (:name x))}))

(start component)
;; => started
(datafy component)
;; => db

(satisfies? clojure.core.protocols/Datafiable component)
;; => true
(satisfies? Component component)
;; => false
So when extending with datafy protocol i get true when checking if the data satisfies? the protocol. However with a custom protocol with :extend-via-metadata true I am getting false in the satisfies? check. Is there something I am missing in the implementation? thanks

seancorfield16:02:34

@fj.abanses satisfies? doesn't work via metadata -- what you're seeing with (satisfies? clojure.core.protocols/Datafiable component) is the implementation of that protocol for Object, and component is an instance of Object.

javi17:02:46

Thanks @U04V70XH6, makes sense now.

seancorfield17:02:34

It took me a bit of digging in the REPL to figure out what was going on!

khardenstine18:02:33

is there a more idiomatic way to spread a map into map varargs? given (defn foo [a b & {:as x}]) (->> {:a 1} (apply concat) (apply foo 1 2)) is hideous

dpsutton18:02:40

(defn bridge [a b x] (apply foo a b (interleave (keys x) (vals x))) ?

dpsutton18:02:38

basically make a more graceful api? or is it a lot of functions so covering them doesn't really work?

noisesmith18:02:19

yes, it's hideous, I think apply concat is nicer than interleave, and my solution is to never use & {}

noisesmith18:02:28

(if I can help it)

noisesmith18:02:29

also I think (apply foo 1 2 (apply concat x)) is nicer than the arrow form, but that's subjective of course

dpsutton18:02:40

agree on the never use & {}. I think interleave would be more meaningful to me when i see this in the wild 3 months after writing it. also agree don't thread but context of actual use dictates that ¯\(ツ)

khardenstine18:02:02

i just run into apis out of my control frequently enough to be irritated by it. wasnt sure if there was an applym or something cute to be done with the core libs

noisesmith18:02:30

no, there's nothing built in

noisesmith18:02:20

@dpsutton perhaps as a compromise (def unwrap-map (partial apply concat))

dpsutton18:02:37

put it on clojars! #shipit

khardenstine18:02:32

ill import it right below leftpad 😉

noisesmith19:02:38

@khardenstine quoting the bot on #clojure irc:

You have to do something like
(defn mapply [f & args] (apply f (apply concat (butlast args) (last args))))
, which just goes to show why unrolled keyword args are a bad idea

🙏 5
dpsutton19:02:26

i just don't know what you gain either. I know common lisp used that style a lot but with map syntax so convenient it just seems worse in every regard

Noah Bogart19:02:15

why (butlast args) (last args)? What does that get you?

dpsutton19:02:37

that's how the map gets flattened into a sequence. args = (a b {:x 3}) (concat (a b) {:x 3}) => (a b :x 3)

👍 5
khardenstine19:02:48

yea i agree the syntax doesnt help enough, it just gets in the way of composition

dpsutton19:02:25

that's why i think the interleave makes a lot of semantic sense

dpsutton19:02:58

(apply f (concat (butlast args) (interleave (keys (last arg)) (vals (last arg)))

dpsutton19:02:14

makes it clear to me that the map gets turned into key value key value ...

dpsutton19:02:23

but it is for sure more work

noisesmith19:02:42

to me it's intuitive that concat on a map would turn it into a list of keys followed by their values

khardenstine19:02:13

incase anyone else code golfing wants a macro:

(defmacro applym [f & args] `(apply ~f ~@(butlast args) (apply concat ~(last args))))

dpsutton19:02:22

why not a function?

dpsutton19:02:18

saving on allocations?

noisesmith19:02:02

this doesn't work if the map isn't a literal, and a big reason to want something like applym is to reuse a default map for the args

khardenstine19:02:31

that would preserve a symbol

dpsutton19:02:36

(let [thing {:a 1 :b 2}]
  (applym f 3 thing))
{:b 2, :a 1}

khardenstine19:02:56

(macroexpand '(applym foo 1 2 k))
=> (clojure.core/apply foo 1 2 (clojure.core/apply clojure.core/concat k))

dpsutton19:02:59

(defn f [a & {:as x}]
        (prn x))

Alex Miller (Clojure team)19:02:53

I use kwargs and & {} all the time, yet never have this issue. can someone back up and tell me a real scenario where you’re doing this?

dpsutton19:02:05

presumably programatically creating the args and then having to use a function that uses & {}

Alex Miller (Clojure team)19:02:22

I guess I tend to use them on the outer layer where it’s people, not programs

khardenstine19:02:43

yea specifically im progmatically calling into an api with kwargs

dpsutton19:02:15

and you prefer (invoke :arg1 :a :arg2 :b) instead of (invoke {:arg1 :a :arg2 :b}) for humans?

Alex Miller (Clojure team)19:02:55

we’ve actually contemplated a small change that would make either automatically work in Clojure

Alex Miller (Clojure team)19:02:18

if you use & {} in the args

dpsutton19:02:10

that might be kinda nice

khardenstine19:02:12

that would be cool

Alex Miller (Clojure team)19:02:29

it doesn’t invalidate any existing code, just a tweak in destructuring

Alex Miller (Clojure team)19:02:52

well, maybe it’s in the compiler, I don’t remember

Alex Miller (Clojure team)19:02:10

possibly 1.11 feature :) but don’t quote me on it

joelsanchez19:02:03

(defn a [b & [{:keys [c d]}]]) would be unaffected I guess

Alex Miller (Clojure team)19:02:41

correct, effect is allowing something new in the invocation

butterguns20:02:14

Anyone have any recommendations: I'd love something to "organize" my requires and imports. Remove unused, and alphabetize would be nice

butterguns20:02:47

I use Cursive, but I don't think it does this

Lennart Buit20:02:00

I installed a plugin for line sorting and ended up putting a newline between (:require and the first actual require

Lennart Buit20:02:26

needless to say, that makes that I now have about three different ways of requiring things

butterguns20:02:41

Interesting. What's the plugin?

Lennart Buit20:02:07

I recall that there is also a leiningen plugin that can complain/fix requires

Lennart Buit20:02:12

but its name left me

dpsutton20:02:16

slamhound? is that still around?

butterguns20:02:52

I think eastwood will do the job at complaining at me, but doesn't fix

Lennart Buit20:02:11

yeah there is a fixing tool, but remembering names is really challenging

Noah Bogart20:02:06

That's a great name, haha

Lennart Buit20:02:25

such a googleable name that I ended up searching for my coworkers slack message :’)

butterguns20:02:10

Ha, yes too generic

Graham Seyffert20:02:09

There are some issues with slamhound and Clojure v 1.10 fwiw

josephg20:02:15

Is there a way to define in the environment where deps.edn is located? Using nextflow and the workaround I'm using is to cp deps.edn to current directory, but it's messy/lazy.

josephg20:02:52

thanks, worth checking