This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-09-13
Channels
- # aleph (3)
- # aws (1)
- # beginners (97)
- # boot (41)
- # cider (7)
- # clara (105)
- # cljs-dev (4)
- # cljsrn (66)
- # clojure (185)
- # clojure-argentina (2)
- # clojure-colombia (15)
- # clojure-czech (1)
- # clojure-dusseldorf (8)
- # clojure-greece (2)
- # clojure-italy (5)
- # clojure-russia (33)
- # clojure-spec (14)
- # clojure-uk (9)
- # clojurescript (75)
- # cursive (6)
- # data-science (1)
- # datomic (12)
- # emacs (2)
- # fulcro (71)
- # funcool (1)
- # jobs (6)
- # jobs-discuss (62)
- # juxt (21)
- # lein-figwheel (1)
- # luminus (9)
- # lumo (41)
- # off-topic (39)
- # om (12)
- # onyx (1)
- # portkey (2)
- # protorepl (4)
- # re-frame (14)
- # reagent (50)
- # ring (3)
- # shadow-cljs (6)
- # spacemacs (38)
- # specter (8)
- # test-check (14)
- # testing (52)
- # unrepl (2)
if you want to do something custom, that’s what deftype
is for
also potemkin's def-map-type and def-derived-map may be of use to you if you're doing that https://github.com/ztellman/potemkin
(doseq [i (range 100000)] (curse!))
I just spent an entire afternoon debugging the following problem (I was half convinced it was a jvm / hardware issue)
(defn foo [x] ...) // foo takes only a arg
(defn bar [ ... foo ...] ... (foo 20)) // foo here shadows global foo
(bar ... foo=hash-map)
so now, I don't get an error because hash-maps can serve as functions
and for the life of me, I couldn't figure out why function foo wasn't being called
question: is there some linting tool / boot / clojure option that will loudly scream at me if a local binding shadows something else ?but what about the times when shadowing is what you want? neither eastwood or cursive warn about shadowing. maybe check on kibit and joker i guess but I doubt they do either
Warning on shadowing is definitely something I’ve considered, but it is what you want freqently.
It would have to be a fairly unobtrusive warning, but it’s bitten me from time to time too.
cursive may have caught this if you didn't use foo anywhere else, ie the (defn foo would be grayed out because not used anywhere
what threw me off was the combination of: foo only took one argument, hashmaps can be treated as functions of 1 arg
so instead of getting a runtime error / exception at where (foo x) happens, I just silently get the wrong answer
@qqq what do you use as an editor?
eastwood will warn about shadowing iirc
doesn't exactly fix your issues, but I use https://github.com/Fanael/rainbow-identifiers to make it so these kind of things jump out at me more easily
limiting it to "and is called as a function" is smart, and would probably drop the number of false positives a lot
heh, what do you know
(defn foo [x]
x)
(defn bar []
(let [foo {:cat "cat"}]
(foo :cat)))
https://github.com/clojure-emacs/squiggly-clojure does have eastwood reporting a warning at the (foo :cat)
linelocal: foo invoked as function shadows var: #'yummly.metadata.ads/foo
@bfabry @tanzoniteblack: thanks for the suggestions
Hi, i've got a plumatic.plumbing question: is there any namespace qualified keywords support for fnk and defnk?
what's the standard way of organizing tests to various files? eg if I have a file component.clj, should I place by deftests into that? or create a separate component_test.clj? If yes, where should that file go to? into the same dir as the component.clj or to a parallel directory structure?
@pwrflx most of the time I use separate file with "_test" suffix located in "test" directory under project root. "test" directory pretty much mirror the "src" dir.
@pwrflx: If you're using leiningen, running lein new <proj>
will give you some hints about that
Looking for some inspiration here. I've inherited a stack overflow that happened at least twice, but it's not a regular occurrence and it hasn't happened since before I arrived.
java.lang.StackOverflowError at clojure.lang.RT.seq(RT.java:507) at clojure.core$seq__4128.invoke(core.clj:137) at clojure.core$filter$fn__4580.invoke(core.clj:2679) at clojure.lang.LazySeq.sval(LazySeq.java:40) at clojure.lang.LazySeq.seq(LazySeq.java:49) at clojure.lang.RT.seq(RT.java:507) at clojure.core$seq__4128.invoke(core.clj:137) at clojure.core$filter$fn__4580.invoke(core.clj:2679) at clojure.lang.LazySeq.sval(LazySeq.java:40) at clojure.lang.LazySeq.seq(LazySeq.java:49) at clojure.lang.RT.seq(RT.java:507) at clojure.core$seq__4128.invoke(core.clj:137) at clojure.core$filter$fn__4580.invoke(core.clj:2679) at clojure.lang.LazySeq.sval(LazySeq.java:40) at clojure.lang.LazySeq.seq(LazySeq.java:49) at clojure.lang.RT.seq(RT.java:507) at clojure.core$seq__4128.invoke(core.clj:137) at clojure.core$filter$fn__4580.invoke(core.clj:2679) at clojure.lang.LazySeq.sval(LazySeq.java:40) at clojure.lang.LazySeq.seq(LazySeq.java:49) at clojure.lang.RT.seq(RT.java:507) at clojure.core$seq__4128.invoke(core.clj:137) at clojure.core$filter$fn__4580.invoke(core.clj:2679) at clojure.lang.LazySeq.sval(LazySeq.java:40) at clojure.lang.LazySeq.seq(LazySeq.java:49) at clojure.lang.RT.seq(RT.java:507) at clojure.core$seq__4128.invoke(core.clj:137) at clojure.core$filter$fn__4580.invoke(core.clj:2679) at clojure.lang.LazySeq.sval(LazySeq.java:40) at clojure.lang.LazySeq.seq(LazySeq.java:49) at clojure.lang.RT.seq(RT.java:507) at clojure.core$seq__4128.invoke(core.clj:137) at
I'm trying to interpret this, obviously lazy seqs and a filter(?) There are only a handful of places in the code using 'filter', and a few more using 'remove', but nothing yet that seems out of the ordinary and would cause this.
remove works via filter, so that's one thing to keep in mind
if you look at the entire stack trace you should see the point where it enters the loop, and that's the best place to start
Yeah, I figured and confirmed that in the 'remove' source, so I've been tracking that as well.
Yes to the entire trace, I fear it was lost and this is the start of what was saved 😛
(doall (nth (iterate (partial filter number?) s) 100000))
will result in a similar looking stacktrace
@hiredman that's certainly possible, I'll re-examine with this in mind. Unfortunately nothing jumped out before - looks like single applications of the filter to the collections in question.
Thanks for the help @tbaldridge and @hiredman
concat doesn't show up in your stacktrace, but it is a common culprit in stack overflows when used repeatedly
@the2bears do you have any non-clojure* stacktraces?
I looked at concat as well, it's used in the code once or twice but I figured since it's not in the trace it's not the issue.
@hiredman, but how to get the 'whole trace'. I'm using your example code that generates a similar trace but any trace I get:
Caused by: java.lang.StackOverflowError
at clojure.core$seq__4357.invokeStatic(core.clj:137)
at clojure.core$filter$fn__4812.invoke(core.clj:2700)
at clojure.lang.LazySeq.sval(LazySeq.java:40)
So I'm not seeing where this actually started. A thing I'd like if I hope to capture this if it happens again.
sure, maybe, I dunno, people keep writing libraries that do it for some stupid reason
Agreed, I'll have to wait to see if this happens again and make sure the whole trace is grabbed at the time.
can anyone help me understand this behavior with lazy-seq (Clojure 1.8)?
user> (def s (lazy-seq
(println "resolving seq")
(cons 1 (lazy-seq
(println "resolving seq2")
(cons 2
((fn [x]
(lazy-seq
(println "boom" x)
(/ 1 0)))
:x))))))
#'user/s
user> (first s)
resolving seq
1
user> (second s)
resolving seq2
2
user> (nth s 2)
boom :x
ArithmeticException Divide by zero clojure.lang.Numbers.divide (Numbers.java:158)
user> (nth s 2)
boom nil
ArithmeticException Divide by zero clojure.lang.Numbers.divide (Numbers.java:158)
One of the later lazy seqs throws an error, but when I try to realize that seq again, the body somehow loses the values in its lexical closure
More pubsub questions. Can a single input-channel handle having more than one publication with different topic-fn? My reading of the docs leads me to believe this is true. However as soon as I create a second pub on the input-ch (with different topic-fn) the notifications stop working on the first pub. Any thoughts?
the "thunk" inside a lazy seq is marked with ^:once
metadata, making it a "once-fn" -- practically this means that the closed-over values disappear and are cleared after the only legit invocation of the function @aengelberg
oh. wow.
but then why is it trying to call the same function again if it errors, if it's designed to only be called once?
why not
there’s a ticket about this I think
but really the general policy is that we don’t make any guarantees about seqs that blow up
I guess I'll make sure my seq doesn't blow up then
that’s not the particular ticket I was thinking of (although it’s the same thing), was thinking of: https://dev.clojure.org/jira/browse/CLJ-2069
this is a good example of a ticket that I have a hard time predicting how Rich will react to it - could as easily be declined as accepted
based on things I’ve heard him say in the past
The reason I encountered this is I had to turn a lazy seq into an iterator that was then passed to a Java class which I guess decided to call .next
despite getting an error...?
Oh wait, it looks like the standard call pattern .hasNext
followed by .next
breaks down when there's an error in the underlying seq.
user> (def i (.iterator ...)) ; same seq as above
#'user/i
user> (.hasNext i) (.next i)
resolving seq
true
1
user> (.hasNext i) (.next i)
resolving seq2
true
2
user> (.hasNext i) (.next i)
boom :x
ArithmeticException Divide by zero clojure.lang.Numbers.divide (Numbers.java:158)
boom nil
ArithmeticException Divide by zero clojure.lang.Numbers.divide (Numbers.java:158)
Seems like this is a standard use case that shouldn't break?
I'll vote for the jira ticket 🙂
some workarounds in your current situation - convert your lazy seq usage to an eduction with transducer (if that’s possible)
or call (clojure.lang.SeqIterator. the-seq)
which is kind of some now unused inside baseball
is SeqIterator
a different impl than the standard .iterator
call?
it is, although it’s hard for me to say if either of these will actually get past your issue without trying them
I'll try it out, thanks
Is the ^:once
metadata on the generated fn
necessary to prevent some sort of memory leak? Or is it just for a perf improvement? I'm curious if I can get around this by manually calling (clojure.lang.LazySeq. (fn [] ...))
instead of (lazy-seq ...)
IIRC, the thunk function will be set to null and collected after LazySeq has invoked it. The ^:once
metadata allows the closed-over values to be collected after they are used and before the thunk function has even finished executing.
I don't think the thunk function is set to null, since as you said, it doesn't cache errors, and I'm getting errors again when I call the seq multiple times
so it must have held onto the thunk
oh, nvm
I assume that if I don't keep any references to the head of the lazy seq itself, I shouldn't need to care about how LazySeq manages its pointers internally?
Thanks for the help, Ghadi and Alex
and @hiredman
hi, does anyone know if it is possible to cancel a promesa promise on the JVM or otherwise workaround the absence of p/timeout
on the JVM?
@U0E98NQG2 I know that timeout
doesn't exist in clj version, but doesn't cancel
still exist? https://github.com/funcool/promesa/blob/master/src/promesa/core.cljc#L225
In which case, it is trivial to implement a p/timeout
which makes one wonder why it isn't included. 🙂
No, there are no alternative option, it is just not implemented. PR is welcome for that part :D
I'd suggest moving this into a thread, since you're performing a blocking call on the KafkaConsumer
But this is unrelated to your GC questions
yes, but also switch your put to a a/>!! to properly handle backpressure
as it is now, if producers create data too fast, you're going to hit an exception on that put!
that makes sense, thanks! I'm reading off the channel as follows:
(a/go
(loop []
(when-let [req (a/<! actions-in)]
(let [{:keys [errors actions statements]} (dispatch req sessions)
rid (:requestId req)]
(if (seq errors)
(a/put! actions-out {..})
(do
(when (seq actions)
(a/put! actions-out {..}))
(when (seq statements)
(a/put! statements-out {..})))))
(recur)))
...
Could that be improved as well?well this is a bit different. In this case your read is using a/<! so that's fine
be very, very careful with a/put! a good rule of thumb: Never use a/put! without also giving it a callback that handles the backpressure
a/put! doesn't wait for a slot to be available in the channel, so it basically creates a unbounded queue, and there's stuff in core.async to throw exceptions when such behavior is detected .
(a/go
(loop []
(when-not @poison
(let [records (.poll ^KafkaConsumer consumer (:poll-timeout opts))]
(doseq [r records]
(a/put! ch (deserialize-fn (.value ^ConsumerRecord r)))))
(recur)))
...
(it shows a properly working GC, imo)
just wasn't sure if there was something I could do on my end to make the consumer not create so much garbage to collect 🙂
It does seem odd to me though that there appears to be about 400MB of garbage created every minute. Most likely due to something else in the system, but that's rather high for a mostly idle app.
400MB of garbage says little about whether it's 400 x 1MB or 40K x 100KB (out of hat values. workload specific)
@dadair classic use case for a memory profiler.
In Yourkit you can capture all allocations over a few minutes and then the profiler will tell you where the allocations came from.
fwiw, I don't spot any huge garbage generating Clojure construct in the code dadair pasted
Agreed, it's probably in some sort of logging library.
and ehm - sorry for sidetracking - is the topic "garbage generating Clojure constructs" interesting to some ? I have dusty personal code in a drawer that was written with the aim of exploring this topic.
Sure, but it has to be done carefully. Stuff like allocation removal in the JVM makes this a tricky subject.
Haha. I used btrace
! I have been way too shy to endure peer review, but looking back, what's the point of keeping it private
I have never managed to get complete certainties about some things. It makes the README a bit hard to write, since the results are very much open to interpretation
Trying to run VisualVM memory profiling but it causes the app to hang and eventually errors out saying it can't establish a profiling connection -- sampling works, just not profiling (but I'll move this discussion to #beginners)
try yourkit first imo, VisualVM is 1) more trouble 2) less actionable results (or maybe its just harder for me)
yourkit is free if you make a good usable open source clojure library - which might be within your reach
there is a personal license available for yourkit that is not advertised on their website. $99/year. that's hardly starving hacker rate, but it might be within your range compared to their pro-oriented rates
use the 30-day free trial period to meditate whether or not that price is worth it to you. it just might.
you may also soon realize your memory issues are not blocking you from doing whatever it is your doing. the free trial period will also cover that, for free 🙂 (premature optimization yada, yada)
agreed, the memory issues aren't blocking. I'm just trying to reduce the bloat of our dev stack as 4 JVMs on a Macbook Pro + IntelliJ + etc can get a bit bloated
its mostly going to impact GC metrics (how often, how long) which are hardly noticeable unless you're doing 60Hz stuff
set smaller java max heap sizes, let the JVM explode whenever it grows outside of what you're comfy giving it
I wouldn't tune my stack to my development environment too hard - it's already enough work to tune it for production environments
I have two maps where some of the map values are byte arrays.How would I go about comparing the maps for equality as (= (byte-array [1]) (byte-array [1]))
returns false? i.e. is there something I could use in clojure or do I need to write something that walks the maps and runs Arrays/equals or equivalent manually?
@mbjarland one option would be replacing each byte-array
with a (vector-of :byte)
which could be done with a map / into or a tree-walk, depending on the complexity of your data structure
@noisesmith…looks promising. Thanks for the pointer
Hello all, is there an easy way to convert some key value of a map instead coping all key-values one by one? [{:a 1234 :b "some" ...} {:a 1223 :b "some" ...}] to {[:a "new_one" :b "some" ...} [:a "new_2" :b "some" ...}]
in this case I´ll convert :a value to new :a value
@fabrao (map #(update % :a f) ...)
? I don’t know how you come up with your new values…
but if you have a function f that takes 1234 and returns “new_one” and takes 1223 and returns “new_2" that will work
@noisesmith that was too easy, thank you, it worked. I see I´m clojure newbie
@fabrao you’ll find that most things that involve data transforms are quite simple in clojure, though sometimes you’ll need to reconsider specific approaches (eg. using associative data structures instead of linear scans, updating a cache of derived data instead of doing in place updates by index, using keyed accumulators in reduce instead of mutating an item as you iterate a collection…)
@noisesmith so updating map is what kind of approaches?
it’s one of the simple things I didn’t list
I only gave examples of the ones that tend to be a little trickier for people
sometimes what helps is thinking of how you would do things in SQL
we have group-by etc.
(update m :a inc) is a lot like update table some_row set a = (a + 1)
ugh - that’s not good SQL but hopefully you get the idea
@fabrao think of it as function composition. Clojure has two basic functions to manipulate maps, assoc
and update
. You would use the former when you want to set the key of a map to a value. It takes a map, a key and a value, and returns a new map with your given value under your given key
If a value is already present in the map for the key you want to assoc, it will override it, e.g.
Now, if you wanted to transform the value under :a
in the map by some function f
(for example, increment it with inc
), you could do this:
But that’s pretty verbose. Clojure core includes the function update
, which takes a map, a key and a function, applies the function to the value under that key, and returned an updated map (as @noisesmith demonstrated)
In your case, you had a vector of maps that you wanted to update. Clojure core has the function map
which applies a function to every entry in a vector, and returns an updated vector
What you wanted to do was to update a key for each map in your vector, so it’s quite natural to use the function map
to then call update
on each entry
This probably doesn’t always apply, but when manipulating data like this try to think of what you would do to update the “most nested piece” (here, maps), and then try to think how you would apply this operation if the piece you want to update is nested in a vector, or another map, etc
You might also want to check out https://github.com/nathanmarz/specter. It’s by no mean necessary to manipulate simple datastructures, but it might give you some insights on how to deal with immutable data
In particular, I think the concept of “navigation” is quite helpful, even when thinking about mapping over a vector of maps, like you did
NB for “navigation” you can check out zippers, which literally let you represent your traversal and updates as an arbitrary series of navigations of the data
zippers add a lot of overhead, whereas specter navigators generally run with near-optimal performance