Fork me on GitHub
#clojure
<
2023-05-04
>
lambdam08:05:34

Hello everyone, Is there a way to get the var associated to a symbol, with the symbol as a parameter?

(ns user)

(def foo 1)

(let [s `foo]
  (var s))
This returns clojure.lang.Compiler$CompilerException Unable to resolve var: s in this context The only way I found is using eval:
(let [s `foo]
  (eval `(var ~s)))
Is there a way to get the var associated to a symbol without eval ? Thanks

p-himik08:05:21

#'foo or (var foo). If by "symbol as a parameter" you mean something that is available only at run time, then (resolve 'foo).

lambdam08:05:29

Thanks. resolve is exactly what I was looking for.

👍 2
lambdam08:05:57

Yes, it was for runtime resolution.

p-himik08:05:07

Note that there are also variations - ns-resolve and requiring-resolve.

👌 2
lambdam08:05:05

I'll check the documentation of those functions. Thanks!

igrishaev10:05:36

The answer really depends on which problem you're trying to solve. There are several ways to do this but they all depend on the context

Søren Sjørup11:05:07

You probably know you cannot take the value of a macro in Clojure:

user=> (map comment [])
Syntax error compiling at (REPL:1:1).
Can't take value of a macro: #'clojure.core/comment
But did you know you can use the value of the macro being defined?
user=> (defmacro m [] ((fn [p] p) m))
#'user/m
user=> (m)
#object[user$m 0x1a3e5f23 "user$m@1a3e5f23"]

p-himik11:05:23

And you can always use @#'comment, but you probably shouldn't. :)

😅 1
Søren Sjørup11:05:25

Thanks, didn’t know that! Also I was intentionally trying to see if that case was covered. Not sure if this is the channel for this kind of stuff…

borkdude11:05:24

Clojure doesn't prevent you from really getting the value of the macro if you need to. The error when using a macro as a hof is just to protect silly mistakes

borkdude11:05:49

(resolve 'doseq)

borkdude11:05:53

Perhaps the compiler could have translated

(defmacro my-inc [x] `(inc ~x))

(map my-inc [1 2 3])
into
(map #(my-inc %) [1 2 3])
auto-magically, but Clojure usually doesn't like magic

Søren Sjørup13:05:25

I think that the problem is when the compiler see’s a call it has to know statically if the var is bound to a macro or not. Otherwise it could end up having to generate code that tests for macros, and that would probably be too slow.

borkdude13:05:55

The compiler already knows that it's a macro at "compile" time

borkdude13:05:15

e.g. try:

(defn foo []
  (map doseq [1 2 3]))
This will give you a warning at compile time, not runtime

Søren Sjørup22:05:37

Yes I think this is the same as (map comment []) I gave above. It’s that only vars can be macros not parameters or local vars. Also you can call your my-inc macro without the #() using this very handy notation:

(map @#'my-inc (range) (range) [1 2 3])
;=>((clojure.core/inc 1) (clojure.core/inc 2) (clojure.core/inc 3))

Ben Lieberman17:05:23

How do I get from ArrayList<java.security.cert.Certificate> to Certificate[]? If I use .toArray from the List interface it gives me an object array. Trying to type hint (incorrectly it seems) with ^java.security.cert.Certificate[] throws an IllegalArgumentException.

p-himik17:05:08

.toArray() returns Object[] even in Java. You have to use the .toArray(T[]) overload.

Ben Sless17:05:19

Call seq then into-array with type?

Ben Sless17:05:10

Or maybe just into-array with type arg will work too

p-himik17:05:29

Or that. :) Yeah, seq isn't needed.

emccue17:05:03

(.toArray certs (make-array Certificate 0))

Ben Lieberman17:05:10

oh nice, yeah I missed this one on clojure docs. If you just enter array it is not in the visible (in the dropdown) options, alas. I will be more careful in the future. Thanks!

emccue17:05:46

oh yeah into-array too

emccue17:05:00

(into-array Certificate certs)

p-himik17:05:09

@U03QBKTVA0N apropos is handy for that and doesn't require getting away from your REPL:

user=> (apropos 'array)
(clojure.core/array-map clojure.core/boolean-array clojure.core/byte-array clojure.core/char-array clojure.core/double-array clojure.core/float-array clojure.core/int-array clojure.core/into-array clojure.core/long-array clojure.core/make-array clojure.core/object-array clojure.core/short-array clojure.core/to-array clojure.core/to-array-2d)
user=> (pp)
(clojure.core/array-map
 clojure.core/boolean-array
 clojure.core/byte-array
 clojure.core/char-array
 clojure.core/double-array
 clojure.core/float-array
 clojure.core/int-array
 clojure.core/into-array
 clojure.core/long-array
 clojure.core/make-array
 clojure.core/object-array
 clojure.core/short-array
 clojure.core/to-array
 clojure.core/to-array-2d)
nil

👀 1
ghadi18:05:59

untested, but this is @U2FRKM4TW’s original suggestion (.toArray x (make-array Certificate (count x)))

igrishaev11:05:31

You can use into-array with an explicit type:

(into-array String ["1" "2" "3"])
["1", "2", "3"]

(type *1)
[Ljava.lang.String;

igrishaev11:05:18

In your case, it would be smth like this:

(into-array Certificate <arr-list>)

Mark Wardle21:05:09

Hi. I have a simple job queue in PostgreSQL. I currently have a thread polling for work at intervals. Is there a recommended async construct that I could use to notify that thread to look before the interval? I was thinking a dropping buffer async channel so a thread would either timeout or get something from that channel and then check for work. Is there something better? I may end up with a separate process running this job queue at some point, but in the meantime, this is only within the same process.

Mark Wardle21:05:17

I also wondered about the LISTEN and NOTIFY in recent PostgreSQL as a way to avoid polling or to effect consequences of jobs in the queue more quickly than waiting for the next poll.

lukasz21:05:53

Yes, LISTEN/NOTIFY is the way to do it - your open session will get notified by PG to let you know about inserts into the job table. I have very very very very rough/alpha code for this, but eventually I settled on polling a bit more often and not using the notifications because in my system it didn't matter as much

👍 2
p-himik22:05:00

Ah, thank you for reminding me about it. Somewhat recently I have opened an issue for Proletarian that ended up being turned into a discussion: https://github.com/msolli/proletarian/discussions/15 I just added a comment there that shows how I use LISTEN/`NOTIFY` in CLJ.

👍 2
Mark Wardle06:05:31

Do you use listen/notify in combination with polling, or rely on it alone for your use case?

p-himik06:05:41

No polling at all.

p-himik06:05:41

Or rather, no polling where it depends on my code. I use Proletarian for some specific things and currently Proletarian relies on polling, so that part's there. But I would really like for it to be gone from the library as well because it pollutes logs if you log query statements.

igrishaev10:05:14

I believe, the best choice would be ScheduledThreadPoolExecutor . You declare an instance of it and (.schedule ...) regular jobs. But if you need to do something RIGHT NOW, you just call (.execute ...) and it gets run immediatelly.

p-himik10:05:05

How is it better than a single listening thread? Or is that an answer only to the OP, without the follow-up question?

igrishaev10:05:08

Ideally, you have a component that wraps that pool; on start, it schedules the jobs, and on stop, it terminates them. There is also an extra method from an interface to run something immediately

igrishaev10:05:55

I may guess that if one uses a cycle in a single thread, they also use Thread/sleep which is ineffective

p-himik10:05:06

It's a single thread though. Regardless of the source of the delay, you won't lose more resources than a single thread consumes. With active polling you will most definitely waste other resources as well.

p-himik11:05:39

And the impl doesn't use sleep, it uses socket timeout. But that doesn't really matter.

igrishaev11:05:17

Also, sorry if it's out from the subject, but still: it's better to avoid running a message queue with listen/notify. This mechanism has plenty of limitations. At least it would be better to track messages through a separate table. Even better, if you app is hosted on one instance, use an atom. If you're in Amazon, you can use SNS/SQS

p-himik11:05:06

Don't know for sure but I understood "thread polling for work" in the OP as if there already is a separate table. But still - could you expand on limitations? I'm using that mechanism without an intermediate table - the thread simply reads a small data piece from a notification and sends it down the line, without doing any "heavy" work. In the realm of "at most once" delivery, doesn't seem like there could be any issues with the approach. And the aforementioned Proletarian does use an intermediate table.

igrishaev11:05:25

I think you can read the limitations of notify/listen in the source code here: https://github.com/postgres/postgres/blob/master/src/backend/commands/async.c

igrishaev11:05:17

especially 3): a listener scans all the notifications filtering them by DB oids, which means linear search.

p-himik11:05:33

Ah, that's fine. :) I'm in control of my notifications, there are no things that I don't want to handle.

p-himik11:05:41

Or perhaps I misunderstood that. In any case, there's just one DB per server in my case, so the database OID is always the same - there's nothing to linearly scan for.

p-himik11:05:02

But thank you for the link, I'll add it to the discussion in the Proletarian's repo.

Mark Wardle12:05:21

Thank you for all of this. I have a deployment on AWS so could switch to using some other off-the-shelf service, but also want my same code to work for a bare-metal on-prem deployment that cannot easily call out to external services. I see the value of the queue in terms of a) durability guarantees and b) decoupling and c) asynchronous processing ... such that a component can potentially send an email without having to use integrant to inject all of the dependencies into every component. I am already using ScheduledThreadPoolExecutor for the polling work, so my issue was looking to kick off work inbetween the polling intervals for certain types of job. I hadn't spotted .execute in that executor so I could see that I could kick off a work run for a specific topic on job queue on a case-by-case basis by leveraging that .execute method as part of the abstract 'queue' service, because all jobs are queued through that in any case. Thanks again.