This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-05-13
Channels
- # aleph (6)
- # announcements (10)
- # asami (3)
- # babashka (111)
- # babashka-sci-dev (20)
- # beginners (28)
- # calva (28)
- # clj-http (1)
- # clj-kondo (23)
- # cljs-dev (16)
- # cljsrn (23)
- # clojure (116)
- # clojure-czech (3)
- # clojure-europe (33)
- # clojure-nl (1)
- # clojure-norway (7)
- # clojure-uk (5)
- # clojurescript (41)
- # community-development (2)
- # cursive (5)
- # datahike (4)
- # datomic (13)
- # figwheel-main (3)
- # fulcro (11)
- # google-cloud (1)
- # gratitude (8)
- # hyperfiddle (14)
- # jobs (2)
- # lsp (22)
- # malli (4)
- # off-topic (4)
- # other-languages (4)
- # pathom (13)
- # portal (40)
- # rdf (11)
- # reitit (1)
- # sci (15)
- # shadow-cljs (7)
- # specter (1)
- # sql (6)
- # xtdb (4)
Can a function like
(defn read-forms-in-file [file]
(let [rdr (-> file io/file io/reader PushbackReader.)]
(loop [forms []]
(let [form (try (read rdr) (catch Exception e nil))]
(if form
(recur (conj forms form))
forms)))))
Easily be made lazy?
Sometimes I just want to read the first form to get the (ns ...)
declaration.You could gave it test whether (and (sequential? form) (= 'ns (first form)))
to stop the recursion. Or even just scan forms until you found such a thing and make that the result of the loop
.
Along as you don't recur
after that, it won't read any further.
Thanks. Good ideas ^^ I could also add a second arity for the max number of forms to read that is counted down for each loop.
Is it possible to add some hook function to nrepl so that I can execute some functions when nrepl starts?
Are you using lein
or clj
?
The former allows :repl-options {:init <expression>}
in project.clj
I don't know enough about nREPL to say how to do that for clj
but I'm sure it's possible...
I guess you could just put code in user.clj
which Clojure itself will run at startup.
Best to put that in a dev
folder and have a :dev
alias or similar that adds :extra-paths ["dev"]
in your deps.edn
file.
Presumably you're using :main-opts ["-m" "nrepl.cmdline"]
to start nREPL?
> I guess you could just put code in user.clj
which Clojure itself will run at startup.
Only one user.clj can be sourced. My plan is to have some global dev.clj
file that can be used in every project, meanwhile without impeding the possibility of each project define its own user.clj
file.
> Presumably you’re using :main-opts ["-m" "nrepl.cmdline"]
to start nREPL?
Maybe I will endup using a :dev/repl as you do. https://github.dev/seancorfield/dot-clojure/blob/develop/dev.clj
Yeah, that's kinda why I went that route -- global code to setup REPLs and customize them...
I have a defmacro that looks like:
(defmacro t [n d] `(def ~n ~d))
(t myname {:name "hi" :fn (fn [] (print "hi"))})
(print myname)
;; {:name "hi", :fn #function[user/fn--54026]}
I'd like to keep the :fn
field un-evaled, but eval everything else. How do I do it?Put quote
around the fn, perhaps?
It seems like even not using ~
evals the fn:
(defmacro preserve-fn-code [m] m)
(preserve-fn-code {:a "b" :fn (defn hi [] "hiya")})
=> {:a "b", :fn #'user/hi}
Good enough for me:
(defmacro preserve-fn-code [m]
(let [my-fn (str (:fn m))]
(assoc m :fn-str my-fn)))
{:a "b", :fn #'user/hi, :fn-str "(defn hi [] \"hiya\")"}
I can easily read that form as a list after the macro.
(defmacro t [n {:keys [fn] :as d}]
(let [d (dissoc d :fn)]
`(def ~n (assoc ~d :fn '~fn))))
Note to self: read https://www.braveclojure.com/writing-macros/
@U04V4KLKC Do you need the dissoc
for any particular case?
(defmacro t [n {:keys [fn] :as d}]
`(def ~n (assoc ~d :fn '~fn)))
(t foo {:fn (println "42")}) ;; => will print "42"
On the classpath, I have a ns
named dev
. Also, inside a a.clj
, it requires b.clj
as dev
. How can I access the original dev
ns inside a.clj
? The conditions in the problem shall hold.
Require it with an alas. So b
has alias dev
and you can require dev
with alias orig-dev
.
why you need so much ceremony? you have a number of options: require b not as dev, explicitly require dev as another-dev etc.
ns-resolve is a great function but it is highly unusual to see in the code so it might confuse other developers how might read this code in the future and also it makes refactoring unnecessary hard because tools like lsp will not see add-libs as a reference
Is it possible to get the code that belongs to a defined function?
(defn hi [] (println "hi"))
I'd like a function magic
that gives me the code:
(magic 'hi)
;; (defn hi [] (println "hi"))
well clojure.repl/source
will do this within some parameters (it's a source file on the classpath)
@U01EFUL1A8M had an interesting take on this question not too long ago: https://clojurians.slack.com/archives/C03S1KBA2/p1651007169818909?thread_ts=1651005353.239669&cid=C03S1KBA2
for the more general case, no - at the repl, clojure compiles the source to bytecode, and the source is not retained
Nooo, I want it for functions in files loaded with load-file
XD
well, same problem
Yup, I see. Perhaps some macro magic can preserve the contents of defn
? I'll think about it.
I mean, you could make your own version of load-file
it's just opening a file and then reading and evaling each form. you could use read+string
instead of read
and then put those strings in a map keyed by the ns/name, and then have a function for looking things up in that map
Or have two passes: one that reads forms and stores the defn
s in a map and then call load-file.
well if you like doing things twice, sure :)
Sounds like less code to me.
aside: clojure spec interestingly has the form
which returns the spec expression. And if I remember correctly it can also be found somewhere in metadata (could be very wrong about this).
Alex, is it possible to hook into the reader to attach the &form
as metadata to vars?
It might even be nice as an official option which defaults to false
, but it could be excellent for tool builders, etc
I believe you can do it with tools.reader ?
actually, maybe not
During dev time I sometimes would love to have this. Especially when learning new stuff. Amazing would be an editor utility that inline expands a function call so to speak.
A canonical format for representing metadata about source code + vars at edit time would be super useful for both documentation and editor tooling; it seems to be reinvented slightly differently by each tool that takes on the task (including the ones I've written). IIRC wasn't there previously discussion about working up a proposal for something similar not too long ago? :thinking_face:
Not that I recall, but I'm wiling to sign a blood pact to vote it up on ask.clojure when one of us requests this feature
I'd argue that not having that slightly breaks the promise of a REPL, where source only works if there's a file attached to it. Where we're going we don't need files
the REPL only promises to read eval and print :)
why are variadic functions in the stdlib defined like this?
(defn complement
"Takes a fn f and returns a fn that takes the same arguments as f,
has the same effects, if any, and returns the opposite truth value."
{:added "1.0"
:static true}
[f]
(fn
([] (not (f)))
([x] (not (f x)))
([x y] (not (f x y)))
([x y & zs] (not (apply f x y zs)))))
Why have specific bodies for 1 and 2 params, and not just the variadic case?usually, this is not something worth doing in your own code
I have a value v that prints to this: #<[email protected] rules/add_a>
. Is there a way to programmatically get the name add_a
without ugly string manipulation?
The string representation is "[email protected]"
so I could
(second (re-find #"\$(.*)@" "[email protected]"))
;; add_a
you can get the class name, then demunge
(-> f class .getName clojure.repl/demunge)
something like that
clojure functions don't print with that #<fnclass@....> syntax anymore, must be some tooling you're using. (You'll still have to demunge)
Ah, then it is hash-p (`#p`) that does it: https://github.com/weavejester/hashp
Is there a way to remove a namespace from a symbol without using string manipulation?
(-> func class .getName clojure.repl/demunge symbol) => rules/add-a
But I'd like just add-a
returnedI notice (when trying to make plumatic/schema work with bb) that schema instantiates records like this:
user=> (defrecord Dude [x])
user=> (Dude/create {:x 1})
#user.Dude{:x 1}
I've never seen that before....The relevant commit talks about performance improvement, among other things.
Pure speculation, but maybe they thought that avoiding map->Record
and going straight for Record/create
(which map->Record
uses) would improve performance.
What do you mean by faster?
There's several http clients you can use. You can see a list of them under "HTTP Clients" at https://www.clojure-toolbox.com/. I usually reach for https://github.com/dakrone/clj-http
Cool, will check that out! I mean it's taking 700ms to do a GET request in Clojure, which seems a bit excessive. The same request takes 80ms to complete in Go, and while I'm not expecting the same sort of performance, I am expecting a bit better than that
These days I recommend Hato since it has no transitive dependencies and it uses the built-in JDK HTTP client.
We've stopped using clj-http altogether at work.
I'm surprised that using slurp
or any of the alternatives would be 10x slower. How are you measuring the difference in timing?
Are you timing the http call in the REPL or via the command line?
Interesting. Is there a large response body? By default, some http clients won't read the full response body unless you specifically consume it whereas slurp will read the full response body. Could that be it?
(asking generally how to ignore a return value in clojure) 😛
I am doing
(doseq [x [1]] (time (client/get "
but that doesn't seem too great
> I put that in a file, and am running it like doing clj -m time
(edited)
Oh. That would explain the timing difference. Most of the difference in timing is just how long it takes for clojure to startup.
For example, if you ran clj -m time
with an empty file, it should take a similar amount of time.
For scripting, there's a great tool called https://github.com/babashka/babashka that you can use to run your scripts and avoid the startup delay.
Sorry, I meant, ignoring the JVM startup time, just going off of what (time)
prints out - usually takes around 1200ms, for both slurp and clj-http
I am running openJDK temurin, which I'm not sure what it is exactly, might that be slow?
FWIW, if I do time curl <that URL>
I get
real 0m2.041s
user 0m0.018s
sys 0m0.028s
beat me to it. my results are:
real 0m1.772s
user 0m0.038s
sys 0m0.033s
I just ran that a few times, redirecting output (so it writes to a file) and it ranges from 1.5s to 3.5s
Even just curl -I
on that URL takes 1-2s. So I'm not surprised that slurp
also takes that amount of time and so does clj-http
.
We should extract the time dilation code from the go runtime. Seems like it might be useful.
In my Go benchmark the thing was cached already, that's why it was taking only 80ms...
No problem! Generally, I would still prefer a regular http library for http requests over slurp.
@U04V70XH6’s hato recommendation sounds interesting. I'll have to add it to my list of libraries to try.
hato ++
this got me the fastest curl response
time curl --retry 0 --raw --sslv3 --tcp-nodelay --tcp-fastopen '' > /dev/null
(inc hato)
it's a good one
it gets down to a second, but without the options it's up around 2s
it's variable either way, for me
not really important to the thread, though 🙂
I get around 1100ms - 1200ms from all the clojure clients (but I am also probably closer in proximity to the server)
The owners of http://olx.ro are probably thinking "Wow, that's a popular page today! I wonder why?" 😆