clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
vemv 2021-06-08T00:04:25.064700Z

Are there important drawbacks to slurping a url? All slurped urls would be http1, unauthenticated. I'd prefer slurp to a lib since under my constraints, the dep tree should be unaltered and there should be JDK < 11 compat

2021-06-08T00:34:38.065200Z

It comes with some weird constraints that are platform-specific to the way the default java http client works

2021-06-08T00:34:47.065500Z

But besides that, if it works for your usecase, I don't see why not.

borkdude 2021-06-08T07:54:15.082100Z

If you need redirects you might also want to use HttpURLConnection

borkdude 2021-06-08T07:54:58.082400Z

https://twitter.com/borkdude/status/1243645733515001858

šŸ‘ 2
quoll 2021-06-08T09:39:40.089200Z

@ben.sless I would like to discourage the normalization of this approach. Yes, I know several tools that do it. As a developer who works in security, itā€™s horrifying

Ben Sless 2021-06-08T09:42:42.091Z

@quoll oh, I did not mean to endorse this approach by way of reference. I find it pretty horrifying as well, which is why I always read the scripts I download before running them. It was more in the vein of analogizing it to (eval (slurp url)) which I find equally horrifying

šŸ‘ 1
quoll 2021-06-08T09:47:23.095900Z

I had forgotten about io/copy but I think thatā€™s ideal. (I always used the byte-array/`loop`/`.read`/`.write` approach taught to me by C, and never addressed in Java). I love @borkdudeā€™s tweet and would not have realized that it could be written along with a comment in 240 characters!

borkdude 2021-06-08T09:50:17.096200Z

I've used this in several tools to avoid a dependency on an http library

šŸ‘ 1
quoll 2021-06-08T09:58:21.105200Z

Itā€™s a pattern that keeps coming up!

borkdude 2021-06-08T09:59:20.105400Z

is it me or are you an early bird?

borkdude 2021-06-08T09:59:32.105600Z

or a night owl :)

quoll 2021-06-08T10:01:28.106400Z

Canā€™t sleep once the sun comes up šŸ˜¢

quoll 2021-06-08T10:02:18.107500Z

Lying in bed trying to convince myself that I want to go out for a run

borkdude 2021-06-08T10:02:50.107700Z

:)

quoll 2021-06-08T12:23:47.110700Z

I made it! 5km!

šŸŽ‰ 3
vemv 2021-06-08T18:03:53.136800Z

> I've used this in several tools to avoid a dependency on an http library Thanks! Looks like a nice option. I see you've contributed to http://github.com/martinklepsch/clj-http-lite/ . I'd almost just go and choose it but slingshot/throw+ seems bit of an odd thing to pull in

2021-06-08T20:33:30.159700Z

Slingshot's pretty cool. A little bit odd sometimes and it wasn't adequate for what I wanted it for, but it's small and not a lot to depend on.

borkdude 2021-06-08T20:34:37.159900Z

@vemv I have a fork of clj-http-lite without slingshot, because slingshot could not run with babashka at the time (and it also loads faster with less deps)

borkdude 2021-06-08T20:35:09.160100Z

https://github.com/babashka/clj-http-lite

šŸ» 1
2021-06-08T20:38:32.161400Z

Hmm, I should think about bb support for farolero. Is there a way to either throw arbitrary objects or extend exception in it?

borkdude 2021-06-08T20:39:42.161700Z

you can throw ex-info exceptions?

2021-06-08T20:41:23.162200Z

Right, one of the key features of farolero in the JVM is that the throwable that's used doesn't interact with blanket exception or ex-info catch blocks, because it extends java.lang.Error. I guess a bb script is likely to be small enough and with few enough libraries as to not make this a huge problem, but ideally I'd be able to throw something that wouldn't interact with catching ex-info.

borkdude 2021-06-08T20:43:36.162900Z

@suskeyhose Let's discuss elsewhere to not distract from the http discussion

2021-06-08T20:43:41.163100Z

šŸ‘

borkdude 2021-06-08T21:52:36.166800Z

(btw, I'm adding java.util.Arrays/copyOfRange to bb now and this was the only remaining blocker to run slingshot with it)

borkdude 2021-06-08T22:01:47.167300Z

$ ./bb -e "(require '[slingshot.slingshot :as s]) (s/try+ (s/throw+ {:type ::foo}) (catch [:type ::foo] [] 1))"
1

borkdude 2021-06-08T22:03:19.167500Z

(I'd have to run its tests to see if it fully works)

vemv 2021-06-08T00:39:11.065600Z

thanks! any specific constraint comes to mind?

quoll 2021-06-08T01:04:10.066300Z

Donā€™t send it to read and eval šŸ˜‰

3
2021-06-08T01:06:00.067500Z

Does anyone whoā€™s more familiar with Clojureā€™s bug backlog than I am know if there are any bugs related to the Compiler interpreting vararg sequences provided to macros incorrectly? this is intended behaviour šŸ™ƒ

2021-06-08T16:55:36.129400Z

@suskeyhose I guess the thing thatā€™s a little ā€œsurprisingā€ to me here is that wrapping the varargs seq in a list or a seq (e.g. ~(list enums) also throws this exception, whereas wrapping with vec doesnā€™t. I know thatā€™s because itā€™s still getting expanded to a list which is then interpreted as a function call, and the [] syntax doesnā€™t have that ambiguity, but it just seems like the compiler could do something better about thisā€¦

2021-06-08T16:57:10.129800Z

So putting the quote there won't make it work quite like you expect, and the reason for this is that the contents of the sequence will never be evaluated, it'll just be the symbols and expressions you passed as arguments to the macro. If you want the arguments to be evaluated but included in the resulting source as a sequence, calling list is the way to do it.

2021-06-08T16:57:46.130Z

So if you use the quote, you just get a sequence of symbols, not a sequence of HttpMethods

2021-06-08T16:58:59.130300Z

Also the reason that wrapping it in a list or seq inside the unquote instead of outside is that it's the same thing, just a list of elements, that gets included in the source code, which means that it'll be evaluated as a call.

2021-06-08T16:59:23.130800Z

Lists and sequences have no ambiguity, they are always function calls.

2021-06-08T17:09:31.131400Z

> and the reason for this is that the contents of the sequence will never be evaluated, itā€™ll just be the symbols and expressions you passed as arguments to the macro. Right, haha, I realized this after sending that message šŸ˜… > Also the reason that wrapping it in aĀ `list`Ā orĀ `seq`Ā insideĀ the unquote instead of outside is that itā€™s the same thing, just a list of elements, that gets included in the source code, which means that itā€™ll be evaluated as a call. Right, itā€™s just surprising to have to treat this completely differently. ~(vec enums) works the closest to how I would expect it to. But if I want to actually use a list (for some reason), I need to do (list ~@enums) (obviously these two functions take different arguments, youā€™d have to do the same thing for vector) > Lists and sequences have no ambiguity, they areĀ alwaysĀ function calls. Definitely, to the compiler. But if you read code like this without knowing this background about macros, you might interpret the intent of the programmer in either manner. But yes, to the compiler itā€™s un-ambiguous

2021-06-08T17:14:04.132Z

Iā€™m not sure how youā€™d actually be able to get this to work with seq. Maybe you canā€™t

2021-06-08T17:17:50.132400Z

anyway - thanks for shepherding me on this journey, haha

2021-06-08T17:20:49.132900Z

Well seq is just a coercion function to turn collections into sequences, so I'm not sure how else you'd like it to work. lists are collections that already implement the sequence abstraction, so there's no difference between seq and list really in this case.

2021-06-08T17:21:00.133100Z

Except that if you had no arguments you'd get nil vs an empty list.

2021-06-08T17:21:25.133400Z

And in that case you could just use (seq (list ~@enums))

2021-06-08T17:23:37.133700Z

> so thereā€™s no difference betweenĀ `seq`Ā andĀ `list`Ā really in this case. Right, the only difference is the argument shape they accept. > And in that case you could just useĀ `(seq (list ~@enums))` Yeah, this just seems like something you shouldnā€™t have to do, but maybe thereā€™s just no way around that given the syntax of the language

2021-06-08T17:25:59.133900Z

šŸ¤· Usually functions aren't too picky about their aguments, so it probably won't often matter anyway.

2021-06-08T17:33:03.135700Z

yeah exactly. Just filing that tidbit away in my head to prefer vectors in macros for function arguments

2021-06-08T01:07:08.067600Z

Specifically, this is the behaviour Iā€™m seeing -

2021-06-08T01:07:56.067800Z

this is as minimal of a reproducing case as I can find so far. also, if thereā€™s a better channel for stuff like this please let me know

2021-06-08T01:08:59.068Z

coercing the sequence to a vector also works -

2021-06-08T01:09:13.068200Z

vemv 2021-06-08T01:10:06.068400Z

No matching method GET found taking 1 args for class com.amazonaws.HttpMethod seems telling. Are you sure this is not an interop problem (instead of a defmacro one)?

2021-06-08T01:10:19.068600Z

Seems to be that something in analyseSeq in the Compiler is attempting to macroexpand the argument sequence

2021-06-08T01:10:38.068800Z

@vemv if you try this with only 1 argument to the macro itā€™s fine as well

2021-06-08T01:10:59.069Z

(defmacro my-macro
  [&amp; enums]
  `(println ~enums))
=&gt; #'user/my-macro
(my-macro HttpMethod/GET)
#object[com.amazonaws.HttpMethod 0x2253f919 GET]
=&gt; nil

2021-06-08T01:11:25.069200Z

Where there is more than 1 argument, the compiler is interpreting the first reference in that sequence as a StaticMethodExpr

vemv 2021-06-08T01:12:33.069400Z

(println ~enums) seems off. ~enums will expand to a list, which at runtime means a call

2021-06-08T01:13:20.069600Z

right. I ran into this because I was trying to pass the list to a function in my macro body

2021-06-08T01:13:33.069800Z

This ends up happening it seems -

(macroexpand-1
  '(HttpMethod/GET HttpMethod/PATCH)
  
  )
=&gt; (. HttpMethod GET HttpMethod/PATCH)

2021-06-08T01:14:05.070Z

The println is just the first function I thought of for this case, it could be anything

2021-06-08T01:15:06.070200Z

the real-life scenario is more like -

`(let [...
       opts# (process-opts ~enums)
       ...
       ]
   ...)

2021-06-08T01:15:56.070400Z

I have to ~enums in order to capture the sequence &amp; enums in the input to the macro

vemv 2021-06-08T01:17:34.070600Z

Perhaps at this point you'd want to formulate the question again describing the end goal From intuition, (list 'quote x) may help but it's hard to tell without a more specific problem to solve

2021-06-08T01:18:41.070800Z

well, my end question is ā€œdoes anyone know of a bug filed for this alreadyā€?

2021-06-08T01:19:03.071Z

afaik &amp; args is perfectly fine to pass to a macro, but it breaks when you pass multiple Enum references

2021-06-08T01:19:32.071200Z

there are multiple ways to work around it it would seem

vemv 2021-06-08T01:21:17.071400Z

> well, my end question is ā€œdoes anyone know of a bug filed for this alreadyā€? sounds like a "xy problem" to me, sorry One thing worth pointing out is that HttpMethod/GET is not an object you can pass around, so it's not surprising that you can't embed it arbitrarily in macroexpansions

2021-06-08T01:21:35.071600Z

Your problem is that you are unquoting a sequence, which is printed as a list, which is evaluated as a function call. Most likely you wanted unquote splicing.

2021-06-08T01:22:03.071800Z

(defmacro my-macro
  [&amp; enums]
  `(println ~@enums))

2021-06-08T01:22:19.072Z

I donā€™t want unquote splicing here - I want to pass the sequence to the function Iā€™m calling

2021-06-08T01:22:34.072200Z

unquote splicing results in the incorrect generated code

2021-06-08T01:23:11.072400Z

I see. Then you would need to do this: (println (list ~@enums))

2021-06-08T01:23:25.072600Z

Because again, you cannot print a sequence into code except as a function call.

2021-06-08T01:24:01.072900Z

letā€™s ignore the println for a moment

2021-06-08T01:24:14.073100Z

one second

2021-06-08T01:24:49.073300Z

Sure, what I'm saying isn't connected to the println. Any time you would like to produce a sequence of items based on varargs to a macro you cannot use it directly in the resulting code because it will be treated as a function call. You have to wrap it in a call to list.

2021-06-08T01:25:26.073500Z

This is why coercing it to a vector works as intended. Vectors when evaluated return themselves, they are not evaluated as a function call.

2021-06-08T01:25:36.073700Z

ah I see

2021-06-08T01:26:02.073900Z

> because it will be treated as a function call Yeah, this is the behaviour Iā€™m seeing, but itā€™s inconsistent

2021-06-08T01:26:14.074100Z

well, it appears to be

2021-06-08T01:26:30.074300Z

In what way does it appear inconsistent?

2021-06-08T01:26:31.074500Z

when I pass 1 item it expands OK. It probably will blow up at runtime is what youā€™re saying though?

2021-06-08T01:27:11.074700Z

Yes.

2021-06-08T01:27:30.074900Z

The item won't implement IFn, or worse, it will implement IFn and it'll give you some result you don't expect.

2021-06-08T01:27:46.075100Z

Causing an exception somewhere that is hard to trace back to the macro.

2021-06-08T01:27:53.075300Z

right, that makes sense

2021-06-08T01:28:32.075500Z

> Any time you would like to produce a sequence of items based on varargs to a macro you cannot use it directly in the resulting code because it will be treated as a function call Why is this the case?

2021-06-08T01:33:28.075700Z

Well any time you evaluate something of the form (a b c) it'll be treated as a function call.

2021-06-08T01:33:36.075900Z

All sequences are printed as lists.

2021-06-08T01:33:46.076100Z

right - yeah Iā€™m playing around with this a bit more right now

2021-06-08T01:33:59.076300Z

And while the printing isn't actually used in macroexpansion, it's a good analogy.

2021-06-08T01:34:11.076500Z

All sequences are treated as function calls when evaluated.

2021-06-08T01:34:39.076700Z

Well, they're treated as calls. Macro, builtin, function, or special operator. But a call nonetheless.

2021-06-08T01:35:13.076900Z

yeah I see. I donā€™t really play around with macros too often is what Iā€™m learning here šŸ™‚

2021-06-08T01:35:39.077100Z

Lol, it's all good. Macros take a bit to really grok.

2021-06-08T01:36:25.077300Z

'~enums also behaves as Iā€™d expect it to, presumably because the wrapping quote preserves the ā€œlistinessā€ (data type?) vs. writing out the (GET PATCH) directly to be interpreted as an IFn

2021-06-08T01:37:44.077600Z

not sure how to accurately describe exactly what ' is doing there that makes it work

2021-06-08T01:39:28.077800Z

hmm I may be incorrect about ā€œbehaves as Iā€™d expectā€ there as well - it ā€œlooks rightā€ printed in the REPL, but isnā€™t actually what Iā€™d want

Ben Sless 2021-06-08T05:16:47.078200Z

Ye olde curl URL | sh As long as you don't care about performance it's okay. If you want to use it to download files open and copy might be more convenient than slurping

jumar 2021-06-08T05:47:25.079Z

Why isn't add-libs et al. part of the official (master branch) tools.deps.alpha distribution? https://github.com/clojure/tools.deps.alpha/blob/add-lib3/src/main/clojure/clojure/tools/deps/alpha/repl.clj#L75

seancorfield 2021-06-08T06:02:44.079800Z

@jumar Because the exact design and integration with Clojure is still being worked on.

seancorfield 2021-06-08T06:03:56.080600Z

Alex has said that ā€œsomething like add-libsā€ may find itself directly into Clojure or t.d.a in a different form at some point.

seancorfield 2021-06-08T06:04:47.081500Z

I periodically ask him to bring the add-libs branch (which I use heavily ā€” see my dot-clojure repo on GitHub) up-to-date w.r.t master.

jumar 2021-06-08T07:23:29.081900Z

Great, thanks for the info!

Eugen 2021-06-08T08:30:57.084500Z

hi, any idea how I can change the clojure crash report path?

Full report at:
/tmp/clojure-8030624471491730958.edn
I am running inside a container and it restarts but there is not enough information to figure out why. Also the files are not preserved in tpm. I would like to write the file to a specific location

JuĪ»ian (he/him) 2021-06-08T08:43:46.084600Z

try --report stderr or the Java property -Dclojure.main.report=stderr`

JuĪ»ian (he/him) 2021-06-08T08:44:27.084800Z

see https://clojure.org/reference/repl_and_main#_as_launcher

kwladyka 2021-06-08T08:58:12.085600Z

This cause memory leak. To be precise memory usage grow faster and it ends with exception Error class: java.lang.OutOfMemoryError

(defmethod ig/init-key ::cli-planner [_ {:keys [supplier cache-key-prefix] :as opts}]
  (declare-queue opts supplier cache-key-prefix)
  (let [p (promise)
        t (doto (Thread.
                 ^Runnable (fn [] (shop-supplier! opts supplier cache-key-prefix p))
                 "cli-planner thread")
            (.start))]
    {:thread t :promise p}))
This not:
(defmethod ig/init-key ::cli-planner [_ {:keys [supplier cache-key-prefix] :as opts}]
  (declare-queue opts supplier cache-key-prefix)
  (shop-supplier! opts supplier cache-key-prefix (promise)))
Why?

kwladyka 2021-06-09T16:11:16.210800Z

ha I found everything was a misleading. I am doing last check but I found bound-fn inside and outside this thread. It looks like this is the source of the out of memory. I donā€™t know why exactly it cause memory leak, but I donā€™t want to have this functions anyway in the code.

kwladyka 2021-06-09T16:11:29.211Z

Thank your for solving the puzzle together šŸ™‚

Eugen 2021-06-08T08:59:18.085700Z

thanks, works like a charm

kwladyka 2021-06-08T09:00:35.085900Z

My first idea is, because promise oustide thread cause garbage collector to not being able to free memory. But if it is correct assumption and why in details? Or maybe because of Thread is returned? :thinking_face:

quoll 2021-06-08T09:56:52.104200Z

One thread should not be an issue, unless you are already right on the boundary of memory usage. Iā€™m a bit surprised at this. Youā€™re throwing away the promise, so the JVM knows that itā€™s not going to be needed, but it will be needed for what shop-supplier! does to it. I think it may depend on how aggressively the JVM can optimize the code. May I presume that using a let wrapper to return the promise (without using a thread) also runs out of memory?

kwladyka 2021-06-08T10:32:53.107900Z

That is what I am trying to figure out, but test take hours šŸ™‚ I can check all possible options, but I hope someone here know the answer šŸ˜‰

kwladyka 2021-06-08T10:35:23.108100Z

But memory behave differently from the beginning. You can clearly see on right part of the chart how memory behave without memory leak with the fix.

kwladyka 2021-06-08T10:39:09.108500Z

*lines are different k8s pods (instances)

Ed 2021-06-08T11:29:26.108900Z

that code is being called by integrant, right? ... does that mean that the results of calling ig/init-key are getting put into a system map in ram or something similar? Because the one that runs out of ram returns the promise, while the one that doesn't, doesn't ... so the content of the promise might be being held onto by the surrounding code ... unless show-supplier! returns the promise it's given??

kwladyka 2021-06-08T11:31:57.109100Z

> that code is being called by integrant, right? right

Ed 2021-06-08T11:32:31.109300Z

how does the promise get deref'd in the second example?

kwladyka 2021-06-08T11:32:55.109500Z

promise is only to stop the thread. It has value ::done for example. The memory consumption by promise is not an issue.

Ed 2021-06-08T11:33:25.109700Z

ah ... fair enough

kwladyka 2021-06-08T11:33:52.109900Z

but it affect the garbage collector somehow . I mean I donā€™t know if promise, but the difference in the code.

kwladyka 2021-06-08T11:34:53.110100Z

And honestly I donā€™t see the reason why. It is deeper dark magic šŸ˜‰

kwladyka 2021-06-08T12:52:32.111100Z

Well if I will figure out this I will add more info here.

šŸ‘ 1
Yehonathan Sharvit 2021-06-08T12:59:02.111700Z

What's the best way to write code that runs only locally but won't run in production?

borkdude 2021-06-08T13:00:21.111900Z

A macro with a compile time check on some condition (e.g. environment variable, dynamic, var, whatever). This allows you to completely elide the non-production code in production.

Yehonathan Sharvit 2021-06-08T13:13:46.112800Z

That's what I'm looking for.

Yehonathan Sharvit 2021-06-08T13:14:44.113800Z

Is there a var that works like that in Clojure (e.g. *assert*) or should I set it manually in my build tool ?

borkdude 2021-06-08T13:17:33.114100Z

I would just make a custom one

Yehonathan Sharvit 2021-06-08T13:22:04.114500Z

why?

borkdude 2021-06-08T13:22:49.115Z

more fine grained control. turning off ALL assertions in production is maybe something you wouldn't want to do

dpsutton 2021-06-08T13:27:22.116Z

I do a new class path root that wonā€™t be on the prod class path. dev.nocommit.logging or whatever

šŸ’Æ 1
1
dpsutton 2021-06-08T13:28:23.117500Z

I keep it gitignored so each person can have what they want there. Precommit hooks can look for nocommit and no forms will compile in CI with any of those namespaces

Ben Sless 2021-06-08T13:52:13.118Z

Try opening it in visualvm. I think that since threads are GC roots if you hold a reference to a thread it won't be collected

kwladyka 2021-06-08T15:09:37.118500Z

This is too complex system. I can run it only in specific environment to observe this memory usage.

2021-06-08T15:52:30.118800Z

in the version returning the promise, who consumes it and when do they stop referencing it?

2021-06-08T16:04:01.120400Z

Suppose my application has two standalone tasks (for example, two http endpoints). Is it normal to call task.start() inside ig/init-key method?

kwladyka 2021-06-08T16:34:32.120600Z

It is just intregrant. The promise is used to

(defmethod ig/halt-key! ::cli-planner [_ opts]
    (log/info "stopping cli-planner thread:" (:thread opts))
    (deliver (:promise opts) :done))
so not really on production.

2021-06-08T16:35:30.121400Z

well anything dereferencing that promise will never return if that deliver is skipped

kwladyka 2021-06-08T16:35:50.121600Z

I am not sure, because I have to wait for results, but it looks when promise is outside thread it affect garbage collector inside thread

kwladyka 2021-06-08T16:36:49.121800Z

the puzzle is only memory consumption, code process things like it should

2021-06-08T16:36:49.122Z

Are there any examples of applying transducers to callback APIs? I know I could wrap the callback API with core.async; but Iā€™d prefer to do something lower level.

2021-06-10T16:15:19.250900Z

is it just my functional brain hallucinating or does this sound like a monadic operation - lifting the procedural logic into an object that can be composed into the file processing pipeline?

2021-06-10T16:17:11.251100Z

in my opinion using a collection abstraction in the middle is still usually the most intuitive thing to work with. my hunch is this would go a lot smoother if you use a collection or queue as your glue, and once the whole pipeline works, a transducer can replace the collection conversion as a performance optimization (if that is in fact needed)

2021-06-10T16:19:21.251500Z

because otherwise my senior engineer side think this smells like it would become the kind of code one ambitious dev will make in a fugue of caffeine and long nights, and nobody will be able to maintain or understand afterward

2021-06-10T16:25:26.251900Z

Well reducers/transducers are similar to monads so itā€™s probably not surprising. I was really asking about how to turn a callback based java parser into a clojure.lang.IReduceInit such that it can take xforms and work with reduce / transduce etc. So I was asking about how to apply those existing abstractions to a new thing, rather than create something new that smells like a monad. Anyway Iā€™ve worked it out and have it pretty much working now.

2021-06-10T16:28:53.252100Z

right, and to be more concrete, my suggestion is that instead of implementing IReduceInit and making a transducing function as your first draft, it would go more smoothly if you do it with a collection or queue in the middle as a first draft, and only reach for that interface and transducers as a performance optimization if needed. acknowledged that this is opinionated advice and if you what you have works and is maintainable then cheers šŸ»

2021-06-10T16:31:00.252400Z

This already is the performance optimization work, after that queue implementation šŸ™‚

2021-06-10T16:31:26.252600Z

though itā€™s not just about performance

2021-06-10T16:32:06.252800Z

oh I misunderstand then

2021-06-10T16:32:26.253Z

what's the "not just about performance" aspect here?

2021-06-10T16:33:18.253200Z

all the other trade offs of using reducers/transducers vs seqs

2021-06-10T16:34:52.253400Z

eagerness, resource control etc

2021-06-10T16:37:15.253800Z

oh to me those aren't issues with seqs, they are issues with laziness (which is already a no go when talking to the outside world)

2021-06-10T16:37:27.254Z

I think I understand what you are getting at

2021-06-10T16:41:15.254300Z

well seqs complect laziness, caching and sequences, so they are issues with seqs šŸ™‚ Though to be fair you said ā€œcollectionā€ and I said ā€œseqā€ šŸ™‚

2021-06-10T16:43:42.254500Z

Anyway the code is pretty simple

2021-06-10T16:44:17.255100Z

implementing IReduceInit isnā€™t hard

2021-06-08T16:38:01.122400Z

there's no garbage collector inside a thread, a value can be garbage collected if it doesn't have a gc root the two ways a promise will effect gc: ā€¢ a thread is a gc root and one that is waiting for a promise deref won't exit if the promise is not delivered ā€¢ a promise can hold arbitrary data, and if the promise is held by a gc root, the data in the promise will be as well

2021-06-08T16:38:49.122800Z

I'm picking on the promise here because the handling of the promise seems to be the only interesting difference between your code snippets

Ben Sless 2021-06-08T16:38:52.123Z

In the first example would it be correct to say the promise is referenced by two threads? The initializing thread and the created thread

2021-06-08T16:39:04.123200Z

right

kwladyka 2021-06-08T16:40:04.123400Z

Not sure if I understand. It looks like the issue is when promise is outside thread, then all data processing in the thread have memory leak. This is not about value in promise at all. promise can have for example value ::done, so it is very small if even appear.

2021-06-08T16:41:02.124200Z

if I take this question literally it sounds like you want to turn the callback API into a transducing context, so you could attach a transducer to it the way you would attach one to into or chan - is this actually what you are talking about?

2021-06-08T16:41:06.124500Z

Essentially I have a callback based parser for a data format, and Iā€™d like to ā€œtransduceā€ (load) it into a connection object with an optional xform

2021-06-08T16:41:26.124600Z

yes precisely

2021-06-08T16:41:44.124800Z

Was thinking Iā€™d just deconstruct the xforms

2021-06-08T16:42:01.125Z

i.e. something like this: (((map inc) conj) [] 1) ;;=&gt; [2]

kwladyka 2021-06-08T16:42:04.125200Z

BUT I am not 100% sure. I have to wait half a day šŸ™‚

2021-06-08T16:42:08.125400Z

is the promise always delivered to?

Ben Sless 2021-06-08T16:42:17.125600Z

You can take a heap dump then analyze it with visualvm to see who holds references to the promise and the thread

2021-06-08T16:42:18.125800Z

where conj would be the reducing function for adding something to my connection.

2021-06-08T16:42:19.126Z

because skipping delivery can make the thread hang

2021-06-08T16:42:28.126200Z

and [] would be the connection

kwladyka 2021-06-08T16:42:44.126400Z

no, it is used only to stop processing in rare cases, so in general it is almost never delivered

2021-06-08T16:43:08.126600Z

@ben.sless holding a reference to a thread is irrelevant, you can literally ask the jvm to list all existing threads, they are gc roots and are only collected if they exit

2021-06-08T16:43:41.126800Z

@kwladyka at this point I don't think the information you have provided is sufficient to narrow down your problem

kwladyka 2021-06-08T16:44:04.127Z

What do you need to know?

Ben Sless 2021-06-08T16:44:33.127200Z

Right, and it isn't daemonized. Still, a heap dump should be a good place to start when hunting down memory leaks and runaway references, no?

dpsutton 2021-06-08T16:44:34.127400Z

could you put your callback into your completing arity?

2021-06-08T16:45:48.127600Z

no, the callback is equivalent to the 2-arityā€¦ i.e. itā€™d need to be essentially for reducing an individual item

kwladyka 2021-06-08T16:46:06.127800Z

I think this is not at all about delivering prmoise. For me it looks like a construct with promise outside thread which is never delivered affect memory usage in thread. The thread is about processing data and this is what consume memory.

2021-06-08T16:46:22.128Z

there are other callbacks for completing etc though which would need to call the completing arity

kwladyka 2021-06-08T16:47:03.128200Z

and this processing have nothing to do with this promise

2021-06-08T16:47:27.128400Z

Pretty sure I can knock this together quite easily actuallyā€¦ but thatā€™s me finishing for today šŸ˜© One for tomorrow!

2021-06-08T16:47:45.128600Z

Just wondered if there were existing examples of this sort of thing

2021-06-08T16:48:22.128800Z

where someone has augmented a java callback api and java aggregating object, with an arbitrary xform.

kwladyka 2021-06-08T16:48:34.129Z

Can you recommend good video / article about garbage collector and how it work?

kwladyka 2021-06-08T16:49:06.129200Z

Even better on clojure examples?

ghadi 2021-06-08T16:56:33.129600Z

callbacks are one-shot, but a transducer transforms an operation that is probably not one-shot

2021-06-08T16:58:58.130200Z

I'm wondering what advantage transducers would have when attached to an API over comp

2021-06-08T16:59:16.130600Z

they have the disadvantage of being a lot more complex than comp

ghadi 2021-06-08T17:04:42.131Z

there's a mismatch here

ghadi 2021-06-08T17:05:43.131200Z

an api callback is a one time thing

kwladyka 2021-06-08T17:17:32.132200Z

Well wait for results of my tests to figure out which code exactly cause the issue.

2021-06-08T17:18:02.132600Z

it depends on the api, a lot of callbacks(listeners) are not a one shot thing

1
dabrazhe 2021-06-08T17:21:19.133300Z

I'm getting a vector of maps with numbers from an API. I am filtering the maps for the numbers above a certain value. When it returns an empty sequence (ie no numbers above the value), I want to receive the number closest to the value. How can achieve this kind of fuzzy filtering?

2021-06-08T17:32:36.135100Z

this seems too simple for what you are asking, but I'd just do (or (seq (filter high-enough result)) [(closest result)])

2021-06-08T17:33:04.135900Z

I put the second conditional in a vector so that the result is always sequential, even though closest should clearly return a single item

2021-06-08T17:34:45.136700Z

in practice this sort of logic is a common reason that you have to change a -&gt;&gt; body into a let block where bindings refer back to previous bindings

kwladyka 2021-06-08T18:04:47.137600Z

hey, I think you have to ask more precisely. This is hard to understand your needs and probably this is why nobody answered.

dpsutton 2021-06-08T18:08:51.138500Z

i have used a priority queue to reduce over a collection keeping the top N items. You could do something like this. Then you're left with either the collection you want, or the largest items if they haven't hit the threshold.

vemv 2021-06-08T18:19:47.138600Z

You could try posting a (redacted) stacktrace here. There are various types of OOMs; perhaps by posting the specific type/cause something can come to mind

dabrazhe 2021-06-08T19:14:35.143600Z

@noisesmith I understand you are suggesting to solve it algorithmically: if filter result is empty? then run closest. This I can do. The thing is, I have this kind of logic in many places for various vectors of maps. I was thinking about some cool functional pattern inside the mapping function : ), or wrapping in the middleware that will always return the closest result

dabrazhe 2021-06-08T19:14:56.143700Z

how do you write and work with a priority queue ?

dpsutton 2021-06-08T19:15:41.143900Z

https://github.com/dpsutton/heap-keep/

dpsutton 2021-06-08T19:16:01.144100Z

it's only useful if you know you only need at most N items

dpsutton 2021-06-08T19:16:18.144300Z

but you just keep the largest N items you've seen so far.

dabrazhe 2021-06-08T19:19:43.144500Z

Thanks, will have a look

2021-06-08T19:21:49.145500Z

that's a lot more complicated and error prone than just using seq and or

šŸ‘ 1
2021-06-08T19:24:17.147600Z

something inside a mapping function as you suggest requires state management and rarely (never?) performs better than seq / or, and is always more complex / error prone

pieterbreed 2021-06-08T19:37:46.150300Z

Hi everyone, with gen-class why does the following with-meta not create the (java) annotations on the generated class ? (The commented reader-macro ^ does work, but I cannot create that from a macro. I'm trying to create gen-class expressions from a macro...)

(gen-class :name
           ;; ^{JsonAutoDetect {:getterVisibility JsonAutoDetect$Visibility/ANY
           ;;                   :isGetterVisibility JsonAutoDetect$Visibility/ANY
           ;;                   :fieldVisibility JsonAutoDetect$Visibility/NONE}}

           (with-meta kafka_testdrive.messages.position
             {com.fasterxml.jackson.annotation.JsonAutoDetect
              {:isGetterVisibility
               com.fasterxml.jackson.annotation.JsonAutoDetect$Visibility/ANY,
               :getterVisibility
               com.fasterxml.jackson.annotation.JsonAutoDetect$Visibility/ANY,
               :fieldVisibility
               com.fasterxml.jackson.annotation.JsonAutoDetect$Visibility/NONE}})

2021-06-08T19:43:51.153600Z

gen-class is a macro, it does it's thing at compile time, with-meta is a function which does things at runtime. Same reason (let (vector a 1) a) doesn't work

2021-06-08T19:43:54.153700Z

OOM errors are not scoped to a specific stack (though I guess in theory you could capture all active stack traces when the OOM happens?) it's really something you want heap profiling data for, as @ben.sless mentions. heap profiling with clojure is hard because the same classes are used everywhere and laziness can mess with the tool's idea of who owns something, but profiling does help

pieterbreed 2021-06-08T19:46:33.155300Z

That makes sense... is there another way I get that ^{...} or the same effect generated from within a macro?

2021-06-08T19:48:10.155500Z

I mean technically you can grab the stack of the code that hit the OOM condition, but that's only sometimes the code that's leaking memory

2021-06-08T19:48:34.155700Z

and sometimes the issue isn't a leak, but rather you are trying to use an algorithm that consumes more memory than you provide the vm

2021-06-08T19:51:27.155900Z

another possible source of the problem you could look into (unlikely but possible) is that Thread doesn't propagate dynamic bindings from the caller's context and always uses the root bindings

2021-06-08T19:53:11.156100Z

you can replace (doto (Thread. ^Runnable (fn [] code goes here)) (.start)) with (future code goes here) - it's simpler and reuses thread objects from a pool instead of creating new ones on every call so it performs better too

2021-06-08T19:53:45.156300Z

and it propagates dynamic bindings from the call context, which is nearly always what you want

vemv 2021-06-08T20:12:21.156600Z

OOMs sometimes are caused by stack consumption which can be accurately correlated with the stack that threw the exception anyway a stacktrace can be useful, there are other reasons also (e.g. sometimes it details the kind, like ran out of metaspace)

dpsutton 2021-06-08T20:16:11.157500Z

I see eastwood has a dependency on {:group org.clojars.brenton, :artifact google-diff-match-patch, :version 0.1} but i cannot find any licensing information for this artifact. doesn't seem to have a repo associated with it through clojars. does anyone know where i could source this information?

javahippie 2021-06-08T20:22:55.159400Z

Does anybody know about an article or some documentation on why clojure.lang.BigInt exists in addition to java.math.BigInteger, but no such thing was done for java.math.BigDecimal? I read some comments that itā€™s done to prevent autoboxing, but iā€™d like to read a little deeper into the reasoning.

alexmiller 2021-06-08T20:38:20.160900Z

yeah, that's it

alexmiller 2021-06-08T20:38:35.161600Z

BigInt uses longs in long range, and BigInteger beyond

šŸ‘ 1
javahippie 2021-06-08T20:41:01.162100Z

Thanks!

vemv 2021-06-08T20:59:37.165600Z

feel free to create an issue on eastwood, we should have a nice dep tree

vemv 2021-06-08T21:00:23.166Z

otoh eastwood is dev tooling, so licensing matters are more relative (i.e. you'll never bundle eastwood to a prod artifact, so one doesn't have to worry nearly as much)

2021-06-08T21:55:09.167Z

Yeah, this isnā€™t a one shot callback; itā€™s an evented parser, that emits an event (calling the callback/listener) on each ā€œformā€ parsed. Control passes to the parser and isnā€™t relinquished until the whole file is processed. Iā€™d really like to load all the forms into a db (hence the connection object); with an optional transformation (hence the xform), but thereā€™s an impedence mismatch due to the inversion of controlā€¦ Similar I guess to why you often need to resort to using PipedInputStream when redirecting input to an output stream. Iā€™d really like to handle this without spawning an extra thread (which is what Iā€™ve done in the past), and I see transducers as a solution to this, as the reducing-function can be passed into the parsers thread. Hence I was thinking Iā€™d write an into-connection function that would wrap all this up nicely.

greglook 2021-06-08T22:26:51.168200Z

Thereā€™s also a hash code issue with BigInteger, IIRC

greglook 2021-06-08T22:27:16.168700Z

yup

user=&gt; (.hashCode (Long. 12345678901))
-539222985

user=&gt; (.hashCode (java.math.BigInteger/valueOf 12345678901))
-539222925

user=&gt; (.hashCode (bigint 12345678901))
-539222985

dpsutton 2021-06-08T22:54:01.169300Z

what's the incantation i'm looking for here: clj -Dillegal-access=debug. is it -J-Dillegal-access=debug?

dpsutton 2021-06-08T22:57:39.169600Z

answer: clj -J--illegal-access=debug

dpsutton 2021-06-08T22:57:57.170100Z

and i'm getting illegal access warnings from clojure.data.xml, which i thought was free of them

seancorfield 2021-06-08T23:03:19.170400Z

clojure.data.xml or clojure.xml?

dpsutton 2021-06-08T23:03:27.170600Z

clojure.data.xml

dpsutton 2021-06-08T23:03:38.170800Z

at clojure.data.xml$parse.invokeStatic(xml.clj:346)

dpsutton 2021-06-08T23:03:53.171100Z

(amongst many other stack frames šŸ™‚

dpsutton 2021-06-08T23:04:17.171500Z

reading in a google group message i was expecting this to not be an issue with clojure.data.xml

seancorfield 2021-06-08T23:04:43.171700Z

Ah, reflective access, not reflection. My bad!

seancorfield 2021-06-08T23:05:55.172700Z

So, yeah, I am surprised. I would have expected reflective access to not be an issue with that contrib lib. @alexmiller?

dpsutton 2021-06-08T23:06:25.173200Z

i'll go post it on http://ask.clojure.org and not bother him

seancorfield 2021-06-08T23:06:32.173400Z

Is it perhaps due to an older transitive dependency or something?

dpsutton 2021-06-08T23:07:45.173800Z

let me check. only three deps but one was cheshire so i'll try again

dpsutton 2021-06-08T23:10:05.174700Z

remained the same. moving to "0.2.0-alpha6" from "0.0.8" actually changed the behavior of the program and it failed to find some things in the poms

alexmiller 2021-06-08T23:11:34.175500Z

Despite the name, you should use 0,2.0-alpha6

alexmiller 2021-06-08T23:13:00.175800Z

Whatā€™s the issue?

dpsutton 2021-06-08T23:14:55.176500Z

gonna have to debug why it failed to hit some info in the poms. but i'm switching now. thanks

greglook 2021-06-08T23:17:24.176600Z

BigInteger has a different hash code than the equivalent Long value, whereas clojure BigInt is identical

greglook 2021-06-08T23:18:03.176800Z

I have a hazy memory of that being given as the explanation for why Clojure needed BigInt instead of just building on top of the Java type

dpsutton 2021-06-08T23:21:27.177400Z

ah, what was previously a :tag groupId is now :tag :xmlns.http%3A%2F%<http://2Fmaven.apache.org|2Fmaven.apache.org>%2FPOM%2F4.0.0/groupId

dpsutton 2021-06-08T23:30:48.178100Z

the bump to "0.2.0-alpha6" does solve the illegal access though. unfortunately breaks all navigation into the csv. but i can patch that. thanks all

seancorfield 2021-06-08T23:33:51.178400Z

Because 0.0.8 didnā€™t handle XML namespaces but 0.2.0-alpha6 does?

alexmiller 2021-06-08T23:41:30.178600Z

isn't what you're showing the opposite of that?

alexmiller 2021-06-08T23:41:48.178800Z

(also fyi, Clojure doesn't use .hashCode, hash is the relevant function here)

alexmiller 2021-06-08T23:42:44.179Z

most likely

dpsutton 2021-06-08T23:44:37.179200Z

yeah. also lots of "\n" in the content

seancorfield 2021-06-08T23:45:59.179400Z

Which also changed because 0.0.8's behavior was incorrect?

seancorfield 2021-06-08T23:46:55.179600Z

I think the whitespace thing broke us when we upgraded ā€” but that was a long time ago I think?

dpsutton 2021-06-08T23:52:40.179800Z

i'm building a dep license concator and having to parse poms. haven't done that in a while. so had been a bit since i'd looked at xml at all, much less parse it

alexmiller 2021-06-08T23:55:31.180Z

there's a flag or something for the whitespace thing

alexmiller 2021-06-08T23:55:45.180200Z

content type to skip or something

alexmiller 2021-06-08T23:57:25.180700Z

I believe that's an example

dpsutton 2021-06-08T23:59:28.181600Z

Oh thank you Alex. I vaguely remembered that but nothing in the docstring was catching my attention