This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-06-12
Channels
- # announcements (4)
- # babashka (7)
- # beginners (55)
- # cider (1)
- # clojure (14)
- # clojure-austin (3)
- # clojure-europe (30)
- # clojure-gamedev (1)
- # clojure-nl (1)
- # clojure-norway (142)
- # clojure-uk (4)
- # clojuredesign-podcast (4)
- # clojurescript (114)
- # conjure (1)
- # cursive (1)
- # data-science (2)
- # gratitude (6)
- # helix (2)
- # hyperfiddle (20)
- # jobs-discuss (6)
- # lsp (21)
- # off-topic (40)
- # pathom (1)
- # practicalli (1)
- # quil (11)
- # rdf (13)
- # re-frame (11)
- # reitit (2)
- # sci (25)
- # shadow-cljs (39)
- # squint (2)
- # tools-deps (73)
What is "tapping"?
https://github.com/clojure/clojure/blob/master/changes.md#23-tap`tap>`, like, printf debugging done right? I’ve take it to be a thing you can do almost anywhere to inspect your values as the program runs. but also I’ve only used it with #C0185BFLLSE so far
Thanks guys. I suppose that's different from "step debugging"? I'm guessing this is the observation of live data while the program is really running?
yup, no interruptions from (tap> whatever)
Fascinating, I'm going to take a look at that
“no interruptions” was vague, sorry. from the docs:
> The tap function may (briefly) block (e.g. for streams) and will never impede calls to tap>
, but blocking indefinitely may cause tap values to be dropped.
The first time I tried tap>
I was a bit surprised that (tap> whatever)
doesn't return whatever
. I was hoping I'd be able to apply it inline without modifying the behaviour of my program
thanks @UK0810AQ2!
I'm late to the conversation, but I've found this invaluable for print debugging when it'd be awkward to wrap tap>
or prn
around forms
https://github.com/weavejester/hashp
The nice thing about tap>
(over any print debugging) is that it can send data to any tap listener, such as Portal, Morse, or Reveal so you can see that data as graphs or tables or structures or whatever -- and most of those will "watch" an atom too, I believe, so you can see it changing in real time.
And you can have multiple tap listeners -- so you can add a print listener in addition to your graphical listener(s).
I use VS Code with the Portal extension, so I have two "live" windows displaying tap>
'd values: one displays all my program's logging (via clojure.tools.logging
) as well as REPL evaluations, and the other displays any explicit tap>
calls inside my program while it's running. I can even do that for remote processes too -- so I can use tap>
on QA and production!
I've been keeping Cider's *inspect*
buffer open for a cruder version of this (it auto-refreshes whenever I evaluatate something). But these suggestions here seem much higher powered
is there any way to put a java annotation on a defn'ed function? using new relic it'd be really nice if i could just write (defn ^{Trace {,,,}} my-function [])
instead of having to create a type for the sake the annotation.
https://clojure.org/reference/datatypes#_java_annotation_support
only deftype
, defrecord
and definterface
support java annotations
defn
defines anonymous class. AFAIK, there is no way to add annotations to it
yeah with new relic currently my options seem to be
1. defn the function with an apostrophe (eg my-function'
), make one interface (with a method invoke matching the defn's arity) and one type implementing that invoke, which then calls my-function'
and def my-function
to call that invoke
2. deftype implementing IFn for the relevant arities then def my-function (new MyFunction)
neither is exactly joyful 😞
https://github.com/quintype/clj-new-relic/blob/master/src/clj_new_relic/core.clj maybe there is an option 3
i guess yeah. their implementation is basically what i described with 1 just they're using one interface with a bunch of arities for invoke instead of one interface per function with just the relevant arities
I think you can also add a :gen-class
to the namespace and attach metadata to the method generated during the aot process. I think you can do that without creating an interface/type first?
i don't think that's an option in this case. i can't expect every namespace defining a traced fn to gen-class
For New Relic specifically, we use https://github.com/RutledgePaulV/newrelic-clj which has a defn-traced
macro, but I blogged about how we used to do that "manually" back in the day: https://corfield.org/blog/2013/05/01/instrumenting-clojure-for-new-relic-monitoring/
Oh wow I didn't know about that library! I was considering rolling my own since both new-reliquary and clj-newrelic were dead. Good to know there's one maintained!
And Paul has been very receptive to feedback! We still have a few "native" New Relic bits in our code but have mostly switched out all our custom stuff to use his library instead.
When using the threading macro, is there a way to get the current value in one of the interior fns? e.g. (let [new-db (-> db (assoc :date date) (assoc :location location) (assoc :status (check-status CURRENT-DB-VAL)))])
(let [new-db (-> db
(assoc :date date)
(assoc :location location)
(as-> db (assoc db :status (check-status db))))]
...)
(just to clarify what as->
would look like here)
Thanks @U04V70XH6!
Is with-open
only working (successfully close resource) with specific object? I get below warning, but only at every other invocation. Consistently.
=> technology.tabula.Page
(with-open [in (io/input-stream pdfdoc)]
(let [doc (PDDocument/load in)
sea (SpreadsheetExtractionAlgorithm.)
page (.extract (ObjectExtractor. doc) 1)]
(type page)))
Jun 12, 2023 10:16:00 PM org.apache.pdfbox.cos.COSDocument finalize
WARNING: Warning: You did not close a PDF Document
Jun 12, 2023 10:16:00 PM org.apache.pdfbox.cos.COSDocument finalize
WARNING: Warning: You did not close a PDF Document
=> technology.tabula.Page
you can macroexpand
the code to see what the end result is, but I think you need to move the doc (PDDocument/load in)
into the with-open
bindings.
with-open
will only call .close
on its bindings.
😇 you are absolutely right i need to put it inside with-open bindings, seems so obvious!
I am trying to learn about Transducers by doing to number crunching on trees. I was wondering if someone could help me understand how to compose transducers, especially ones with shared pipelines.
;; A tree of all my dependencies for my project
(def project-data (project-map {:deps "deps.edn"}))
;; This function returns the top level keys, and a count of all the nodes contained in their children
(into {}
(map (fn [key]
{key (count
(filter map?
(tree-seq map? vals (get project-data key))))})
(keys project-data)))
=>
{:user-edn 363,
:root-edn 13,
:master-edn 391,
:project-edn 24,
:basis 791,
:output 0,
:size 0,
:combined-aliases 1,
:lib-map 197}
;; Here is a similar function that I converted to a transducer:
(defn count-map-elements [m]
(transduce
(comp (filter map?) (map count))
+
(tree-seq map? vals m)))
=> 21 (should be 13, but I think I am counting more nodes)
In the transducer, how do people go about making the middle section polymorphic? If I am processing a tree that is dozens of Gigabytes in size, I don't want to walk the tree N times for N functions.
I am trying to figure out how to compose my Transducers for a single pass. Here is some not working code for an example:
(transduce (comp
(filter map?)
(map (3-different-functions)
(3-separate-functions-depending-on-previous-step)
(shared-final-step))
Does anyone have design recommendations for shared transducer pipelines such as this?Honestly, I would probably start with just doing multiple passes and see if it's actually a bottleneck.
However, there are some libraries that can help here:
• https://aphyr.com/posts/360-loopr-a-loop-reduction-macro-for-clojure (not necessarily transducer based, but still focused on a similar problem)
• https://github.com/cgrand/xforms, usually, you can find any transducer related "missing pieces" here. Specifically, check out transjuxt
.
I use`map`to construct a sequence of calls. These function calls can span for some time, so I would like to see some intermediate outputs. I noticed that the println
calls will only be shown after the whole map is completed. Please see the example below. My question is how to make the println
results shown as each call is complete.
(let [f (fn [x]
(Thread/sleep (* x 1000))
(println x))]
(map f (range 10)))
this is a situation where doseq
is more appropriate. map
is lazy - the sequence of calls will not actually happen until the sequence is realized. frequently, when evaluating code in the REPL, people (myself included) forget that the "print" part of "read, eval, print, loop" is forcing the realization of a lazy sequence.
doseq
is eager - it will start working "right away" and you'll see the intermediate results in the manner that I think you expect.
if you want something similar to map
that's eager and returns the collection of results of the function calls, then mapv
is an easy replacement.
That's good. Actually, I plan to use pmap
to parallelize these calls. Is there any function I can use for pmap
?
you'll see some extremely weird output if you try to use println
with pmap
, as multiple threads are pushing character streams to the default writer concurrently and their input will be interspersed.
(try it with (doall (pmap (fn ...) ...)
and see for yourself)
Also, I used mapv
and realized that it would force the effect. Yet, it is unclear to me why the lazy map
would not realize the println
calls even these functions are being called.
Good... I will try (doall (pmap ...))
and report back.
try doing (take 3 (map ...))
- it will show you that the lazy sequence is only evaluating as much of the sequence of calls as is needed, without evaluating the later calls.
That's the part I don't understand. In my real example, these functions are processing files. Even these files are processed, aka, the functions are called, the println
outputs are still not shown.
Funny things sometimes happen to println, especially in multi-threading situations or connected to an NREPL or both. Instead of println, you can try swapping an atom or using a Java logging library (e.g., via clojure.tools.logging).
Thanks the heads-up. It could well be the case. I will check out them.
@UFTRLDZEW I just tried (doall (pmap ...))
and it does not help. Still the intermediate output will only be shown after the whole evaluation is done.
> That's the part I don't understand. In my real example, these functions are processing files. Even these files are processed, aka, the functions are called, the println outputs are still not shown.
>
If you map over something lazy, then map
's own laziness will prevent unnecessary underlying calculations. But if you map over something eager, it doesn't make that thing not eager any more.
The eager evaluation will run first in full, and then map
will lazily pick values from the result.