Fork me on GitHub
#clojure
<
2019-01-30
>
ahungry04:01:43

Is there a good way to convert a qualified map into an unqualified one?

ahungry04:01:22

Like, if I want to get some sample data via gen/sample or gen/generate and work on qualified specs, but turn that output into plain unqualified maps?

seancorfield04:01:48

If you know you have a flat map, with no nesting, (reduce-kv (fn [m k v] (assoc m (keyword (name k)) v)) {} my-map) will probably do. Never mind, just realized you explicitly asked about a nested hash map. But there's an argument for not nesting maps like that...

dpsutton04:01:39

beware that you can introduce collisions if you have something like {:database/id 4 :somethingelse/id 6}

john04:01:05

yeah, that should probably go in the doc string

dpsutton05:01:17

also, be aware of keywords in values. {errors/ui [:error/missing-payer]} should that become {ui [:missing-payer]} or {ui [:error/missing-payer]}

lilactown05:01:36

how offended do people feel about the idea of composing server-side “components” (a la component/integrant/mount states) using hiccup?

sergey.shvets06:01:22

Hi, I have a question about defmulti and defmethod in different namespaces. It seems that if you use defmethod in a separate namespace from defmulti then something in your app has to require this namespace, so defmethod actually runs. Is this true? And if yes, is there a best practice to avoid circular dependency in this case? Can't find the answer in docs or somewhere else...

Lennart Buit07:01:20

Yeah I believe all ns’es that define defmethods for a defmulti must be required for the branches of the multi to be available

sergey.shvets07:01:23

It does work when you require "branch-namespaces" but you can't require them from the namespace where defmulti is, due to a circular dependency. Requiring them elsewhere creates an implicit dependency that will eventually break with some changes.

sergey.shvets07:01:39

Kinda puzzled how to overcome this.

Lennart Buit07:01:53

Right, why do you want to use the defmethod that you are dynamically extending in the namespace that you are defining it in

Lennart Buit07:01:58

could you maybe resolve that

sergey.shvets07:01:28

I have few text formats that I serialize/deserialize to the filesystem. They aren't all defined at this point and will evolve as an application evolves. So, my idea was to define multimethod for every operation and implement it for each format in the separate namespace.

Lennart Buit07:01:14

right, I am not challenging the use of a multimethod. What I think what could resolve your circular dependency is moving the definition of the multimethod away from its use.

sergey.shvets07:01:33

Yes, but then namespace where I use the multimethod has to require both the definition and the implementation namespaces?

sergey.shvets07:01:01

or am I missing something?

dpsutton07:01:49

i don't think that's actually true. the namespace needs to require the defmulti ns but it does not need to require the ns where the defmethod is defined. That namespace has to have been required already, but not necessarily here.

Lennart Buit07:01:02

^right, that is true, thanks for that clarification

dpsutton07:01:42

so you could require all of the implementing namespaces in your start up and be assured that they are all ready to go

sergey.shvets07:01:57

that will solve the problem, I'm just concerned that later someone can remove this "require" because they won't be explicitly used in startup namespace.

sergey.shvets07:01:20

That what I meant by implicit dependency.

Lennart Buit07:01:50

Also, I think my suggestion would work, I would have a ns use that depends on the namespace define + all namespaces extend, where each extend namespace depends on define. I don’t think there is a circular dependency there, but I am lacking some paper to draw it out ^^

sergey.shvets07:01:02

If there is no better way, I can live with this solution of course.

sergey.shvets07:01:00

Yes, that will work. Thanks, @lennart.buit and @dpsutton

seeeturtle08:01:56

hey i have a question about letfn

seeeturtle08:01:34

as i search around, i knew that letfn is for use of function that is not defined yet. (something like mutual recursion)

seeeturtle08:01:59

but, if i try mutual recursion with let, it perfectly works!

seeeturtle08:01:15

why does this work?

seeeturtle08:01:37

(let [odd? (fn [n]
               (if (zero? n)
                 false
                 (even? (- n 1))))
        even? (fn [n]
                (if (zero? n)
                  true
                  (odd? (- n 1))))]
    (odd? 5))

seeeturtle08:01:54

so if letfn supports ahead-of-defintion use of function and let doesn't that code shouldn't work

noprompt08:01:04

Cause the first even? is clojure.core/even?.

noprompt08:01:11

Call the bindings f and g and it will 💣

seeeturtle08:01:54

btw, then letfn doesn't use existing function if there is definition in it?

noprompt08:01:47

Only one way to find out. 😄

noprompt08:01:45

Night! 😴

seeeturtle08:01:27

gonna play with repl 🙂

seeeturtle09:01:01

yup it ignores existing function

seeeturtle09:01:06

(letfn
          [(odd? [x] (even? x))
           (even? [x] true)]
        (odd? 5))
;= true

seeeturtle09:01:54

it has weird naming but nvm

witek10:01:35

Hi. I am looking for an elegant way to construct a map based on multiple conditions. This is what I have:

(defn build-foo []
  (let [m {}
        m (if cond-1?
            (merge m {:foo 1 :bar 2})
            m)
        m (if cond-2?
            (merge m {:x 1 :y 2})
            m)]
        ;; continue with cond-3 ... cond-n
    m))

Lennart Buit10:01:44

(use three backticks to format code spanning multiple lines)

bronsa10:01:55

(merge m (when cond1? {:foo 1 :bar 2}) (when cond-2? {:x 1 :y 2}))

👍 10
mpenet10:01:51

(cond-> m (foo? x) (assoc ....) (bar? y) (assoc ..)) or (cond-> m (foo? x) (conj ....) (bar? y) (conj ..))

mpenet10:01:59

imho merging with (only) 2 args is quite often a smell, especially when you are sure the first arg is a map (non nil)

👍 5
victorb10:01:27

Hm, so it seems clojure.trace/trace-ns doesn't accept a namespace as a string. I currently have a list of namespaces in a list with strings. I would like to pass them to trace-ns inside a doseq, but always get "No namespace: n found" (`n` is the name of my variable, seems trace-ns doesn't read the value of the var but instead the symbol itself)

erwinrooijakkers13:01:51

user=> (find-ns (symbol "user"))
#object[clojure.lang.Namespace 0x386f0da3 "user"]
user=> *ns*
#object[clojure.lang.Namespace 0x386f0da3 "user"]

victorb13:01:26

Many thanks. Will try it out in a bit and report back

erwinrooijakkers13:01:32

So it probably works when you map symbol over your list of strings (map symbol namespaces)

victorb10:01:37

Guessing I'm looking for the opposite version of ns-name

Tom H.13:01:00

hi folks, I have a clojure project that has been packaged with lein jar, how do I run it? 😄 I've only ever used lein uberjar

Alex Miller (Clojure team)13:01:08

are you running it with lein or separately? if the former, lein run probably does the right thing. if the latter, you need to include the jar and all transitive dependency jars on the java classpath, then run the main class. java -cp my.jar:foo.jar:bar.jar my.mainns

🍻 5
Tom H.13:01:57

I am using lein. Does lein run use java classes in :java-source-paths? I was thinking the instruction to use lein jar (from the creator of the project) was to also include a separate java class.

Alex Miller (Clojure team)14:01:13

I think lein run includes the class output target dir in its path and Clojure will use the classes if it finds them (before the .clj)

Alex Miller (Clojure team)14:01:28

oh, sorry you asked about java classes

Alex Miller (Clojure team)14:01:48

I would expect it to only find java classes in the target output, not in java source paths

👍 10
Tom H.14:01:20

I've managed to get the project running with lein repl

Alex Miller (Clojure team)14:01:25

(but don’t take my word for it, that’s just my expectation)

👍 5
Robert A. Randolph16:01:07

I have a vector of hash-maps that I wish to namespace qualify, and I'm curious about how to do this idiomatically (`(map #(reduce-kv ...))`?), and why the first example here does not work:

(reduce-kv #(assoc %1 #:ns{%2 %3}) {}  {:k "v"}) ;; => CompilerException java.lang.RuntimeException: No such namespace: ns etc..

(reduce-kv #(assoc %1 :ns/%2 %3) {}  {:k "v"}) ;; => #:ns{:%2 "v"}

(reduce-kv #(assoc %1 (keyword "ns" (name %2)) %3) {}  {:k "v"}) ;; => #:ns{:k "v"}

Alex Miller (Clojure team)16:01:47

the #:ns{} syntax is a literal map syntax. that is, it’s not a property of the map, it’s just a syntactic alternative when writing literal maps.

Alex Miller (Clojure team)16:01:21

similarly, :ns/%2 is a keyword whose name is literally %2

Alex Miller (Clojure team)16:01:30

both of these are things happening at read time, with no eval

Alex Miller (Clojure team)16:01:58

the last one seems like a fine solution

Alex Miller (Clojure team)16:01:32

there are some common utility functions (often called map-keys or map-vals) that apply a function to all of the keys or vals of a map and that might be an easier way to think about it. You can find those functions in many of the utility libs (medley, useful, etc)

Alex Miller (Clojure team)16:01:28

something like (map-keys {:k "v"} #(keyword "ns" (name %)))

Robert A. Randolph16:01:04

excellent, thank you

timvisher17:01:42

Is there any way to get a list of loadable classes on the classpath easily without relying on a library like compliment? The code in compliment is surprisingly complicated and this feels like something that the JVM should be able to do natively via the Classloader interfaces or something?

Alex Miller (Clojure team)17:01:41

classloaders generally don’t have a notion of what can be loaded, just what has been loaded

Alex Miller (Clojure team)17:01:11

the classloader interface intentionally abstracts the notion of “where classes come from” and they could come over the network where there is no way to enumerate them all

Alex Miller (Clojure team)17:01:23

or out of a database (I did one of those once)

angrygami18:01:26

Hi all. I have a problem in my application. Some sort of classloader memory leak. I see that amount of classes is constantly increasing and heap dump is dominated by entries like clojure.core$eval100009 with no instances. How can I figureout what is generating this classes?

hiredman18:01:14

you are calling eval in a tight loop somewhere

angrygami18:01:41

Well I guess I do 🙂

angrygami18:01:26

but not explicitly apparenlty. My code have only one call to eval and it is not in any sort of tight loop...

angrygami18:01:01

that is why I ask "how to figureout what is generating those"

hiredman18:01:06

you are running that code over and over again more often then you think you are

angrygami18:01:53

I doubt that this is in my code, but I'll double check...

noisesmith18:01:02

I would drop any library that uses eval outside some sort of start-up DI type scenario, runtime eval is a code smell

angrygami18:01:41

This is easier said then done 🙂 how to detect which library?

noisesmith18:01:21

you can (for debugging / local purposes only) replace clojure.core/eval with a function that logs stats about usage (including stack trace) then calls the real eval

hiredman18:01:34

libraries are basically garbage, so just get rid of them all

💯 20
angrygami18:01:26

nice advice, cant follow unfortunately 🙂

hiredman18:01:09

just start naming off the libraries you use until we yell bingo

noisesmith18:01:11

regarding how to replace eval with logging version, alter-var-root is pretty straightforward to use

angrygami18:01:32

What if this eval is binded inside some function? Will alter-var-root help?

hiredman18:01:02

but seriously, I would look at your own use of eval

👍 5
noisesmith18:01:08

yes, functions compile to code that looks up vars - the gotcha is if eval was provided as an arg to a first class function - which is rare

noisesmith18:01:44

agreed with @hiredman and logging eval usage would expose that this is the issue as well

angrygami18:01:57

I doublechecked my usage of eval - it only happen once during initial app compilation

Alex Miller (Clojure team)18:01:08

and a caveat that Clojure core itself is direct linked compiled so calls within core won’t see changes from alter-var-root

noisesmith18:01:51

(ins)user=> (alter-var-root #'eval (fn [v] (fn [& args] (println "calling eval on" (pr-str args)) (apply v args))))
#object[user$eval151$fn__152$fn__153 0x158a3b2e "user$eval151$fn__152$fn__153@158a3b2e"]
(ins)user=> (eval '(+ 1 1))
calling eval on ((+ 1 1))
2

noisesmith18:01:26

@alexmiller true but I think we can trust that clojure.core isn't using eval in a sloppy way

hiredman18:01:01

I dunno, maybe he is using the socket repl as an rpc

Alex Miller (Clojure team)18:01:06

Java profilers can also help you with stuff like this (YourKit, JProfiler, etc)

angrygami18:01:20

I found what is causing my issue. Thanks @noisesmith. Now have to think how to avoid that. Btw culprit is dynamic sql generator from yesql

👍 5
noisesmith18:01:42

I would be surprised if whatever yesql is doing couldn't be replaced by a higher order function rather than using eval, I'm disappointed to hear it's doing things that way

noisesmith18:01:47

also, hugsql is newer and supposedly addresses some design issues from yesql

noisesmith18:01:56

(but works similarly)

noisesmith18:01:29

even yesql's readme mentions hugsql and the fact that hugsql is actively maintained unlike yesql

angrygami18:01:37

Well, problem is little bit deeper than yesql. I attached template engine (comb) to yesql that give me dynamic sql generation (yesql can't do that out of the box) and this lib is actually main culprit. I like this feature a lot, though I need to use it more carefully because of this problem... Hugsql is way too much rewrite for me, maybe I'll consider this later

timvisher19:01:40

@alexmiller Ah that's interesting. I guess that's why compliment has all the code it has for finding jars and class directories and such and just lists them?

josephg20:01:28

I spent way too much time getting lumo working on an older server without testing the clojure command to run scripts. It doesn't even take a second, and I can use all the java libs... Slow start-up time is barely noticeable if you aren't using lein and not too many deps.