Fork me on GitHub
#clojure
<
2024-04-18
>
Aya12:04:27

Hey guys Calva has started turning logs into comments and makes inspecting objects frustrating, does anyone know how to switch the comment logs off? e.g if I do` (pprint {:foo bar})` Calva will log

;{:foo bar}
prefixing my log with a comment the plugin didint do this before but it updated and started doing this does anyone know how to turn this setting off?

mmer12:04:06

probably should ask this on he #CBE668G4R channel

p-himik12:04:08

Seems like the same thing has already been discussed in this thread: https://clojurians.slack.com/archives/CBE668G4R/p1712080447688969

Aya12:04:28

Thank you guys I had no knowledge of this

pez12:04:39

Hi! There are several options: • Get used to it being a line comment and enjoy that it keeps the document structure safe from unbalance introduced by stdout things. • Switch to an output destination that does are not sensitive to unbalance, best option there is the Terminal output, me thinks • Opt in on accepting the risk of unbalance in the repl window with the calva.legacyPrintBareReplWindowOutput setting. See https://calva.io/output/ about your options for output destinations.

pez12:04:16

I am curious about: > makes inspecting objects frustrating Can you elaborate a bit? Let me also plug this new feature here: https://calva.io/inspector/

Aya12:04:46

When I log I like to collapse and expand certain properties because I log large objects

Aya12:04:18

so when its commented i have to scroll through a large object

pez12:04:14

I see. Then the repl window with the lagacy bare print may be your best option.

Aya12:04:43

thank you

🙏 1
pez12:04:18

It would be nice to support logging those objects directly to the inspector. Since you have it in the repl window, you can do Add Current Form to Inspector, but it would be nice to be able to do it without printing it in the REPL window first. I’ll have to think about this a bit. 😃

Aya14:04:17

Awesome thanks

Aya14:04:49

If we can have a dev tools like way of inspecting objectes that would be great

pez14:04:51

What would a dev tools way of inspecting objects be like?

Aya15:04:01

when you log an onject it starts out collapsed then you can expand and explore properties as you like, you can even select and copy some porperties, basically full control over the obejct

pez15:04:53

That’s not possible with VS Code files, unfortunately. That’s why we crated the inspector. But it is something to consider if we implement some other output option. cc @U9A1RLFNV

👍 1
Aya15:04:32

Ok got it 👍

bringe16:04:27

@U01RVS03CAZ You also might want to check out Portal for structured output like that, if you haven't yet.

seancorfield17:04:08

@U01RVS03CAZ I have logging wired up to tap> values -- and use #C0185BFLLSE for inspecting / visualizing my log data.

metal 1
respatialized13:04:56

From a language semantics perspective, I understand why this is necessary, but it still doesn't seem quite right: 😅

(number? ##NaN)
;; => true

vlaaad13:04:18

what else would you expect from a value that is not equal to itself?

🤯 2
2
😕 1
cjohansen13:04:30

(js/isNaN ##NaN)

p-himik13:04:35

@U9MKYDN4Q Both Clojure and ClojureScript now have NaN? function.

👍 1
jpmonettas13:04:29

I think not being equal to itself is a good thing, so you don't end up in weird situations like :

(defn foo [n]
  (let [a (+ n 1)
        b (+ n 5) ]
    (= a b)))
where (foo n) could return true when n is NaN like in (Math/sin (/ 1.0 0.0)) Now the (number? ##NaN) returning true is weird, although I can see it is referring to NaN being part of the "numbers domain" in the sense that can only be returned by operations between numbers

Thierry13:04:52

Wrong channel, my bad

p-himik13:04:55

Note that there's #CBE668G4R.

Thierry13:04:39

ah yea, you are indeed correct. No clue why this posted here, could have sworn I was in Calva. Will move this there. Thanks for the headsup!

👀 1
p-himik13:04:30

No worries.

🙌 1
Karol Wójcik13:04:07

Hello Dear Clojurians! I just reported the following error to GraalVM team: https://github.com/oracle/graal/issues/8801 It seems the mix of reflection entries for java.lang.UUID and clojure.lang.Keyword plus Clojure 1.11.2 makes this error happen: Caused by: java.lang.NullPointerException: Cannot invoke "clojure.lang.Var.isBound()" because "clojure.lang.Compiler.LOADER" is null What is very interesting the downgrade of Clojure to 1.9.0 doesn't produce any error so far. Here is the reproduction: https://github.com/FieryCod/graalvm-repro

Noah Bogart13:04:54

you should post this to http://ask.clojure.org, that's the best place for such bug reports

Noah Bogart13:04:14

alex miller will still probably see it here but he'll recommend the same lol

Karol Wójcik16:04:30

oh right, thank you so much @UEENNMX0T, this is really good advice!

pez14:04:39

👋 I want some input on function “signature” design for a thing. Consider this:

(defn some-svg [{:keys [size type bells? whistles?]}]
  (let [color (type->color type)]
    (svg-hiccup {:size size
                 :bells? bells?
                 :whistles? whistles?
                 :style (merge {:fill color
                                :stroke :darkgrey}
                               (when-not bells? {:fill-opacity 0.25})
                               (when whistles {::stroke-width 6}))})))

(defn some-svg-string-1 [{:keys [size type bells? whistles?]}]
  (dd-string/render (some-svg {:size size
                               :type type
                               :bells? bells?
                               :whistles? whistles?})))

(defn some-svg-string-2 [props]
  (dd-string/render (some-svg props)))
In one part of my program I use the hiccup of the SVG, in another part I need the rendered string. Regardless, they need the same input. some-svg-string-1 is more typing and syncing, but the signature is easy to see in the IDE. some-svg-string-2 is less duplication and typing, but the IDE won’t help me much with usage. (The IDE I am using does not dig one further step and show me the actual signature, maybe it should.) Which one would you go with and why? Are there alternatives I am missing?

jpmonettas14:04:58

maybe a combination ?

(defn some-svg-string-1 [{:keys [size type bells? whistles?] :as props}]
  (dd-string/render (some-svg props)))
which also doesn't need to allocate a new map in case this goes into a loop. But then you will have to deal with the linters complaining about unused bindings

vemv14:04:37

I'd choose the latter and then alter its var metadata to have synthetic :arglists. I don't know if e.g. clojure-lsp would understand that pattern, but it should (and a hybrid editor like Calva is in a good technical position to bridge runtime insights with static analysis)

jpmonettas14:04:46

yeah, it would be nice if this could be done :

(defn ^{:arglists '([{:keys [size type bells? whistles?]}])} some-svg-string-1 [params]
  ...)
so it didn't require an extra (alter-meta! ....)

pez14:04:34

I like the combo, @U0739PUFQ. However, the linter doesn’t 😃 I can do it like so:

#_{:clj-kondo/ignore [:unused-binding]}
(defn some-svg-string-1 [{:keys [size type bells? whistles?] :as props}]
  (dd-string/render (some-svg props)))
Which is fine for this particular case, but not if there are more bindings that I want linter-help with keeping track of…

pez14:04:42

But argslist was the way. This works great with clojure-lsp’s help, til

(defn ^{:arglists '([{:keys [size type bells? whistles?]}])} some-svg-string-1 [params]
  ...)
Thanks, both of ya!

pez14:04:51

And thanks @UKFSJSM38 and the #CPABC1H61 team! 🙏

Ed14:04:36

How about naming the thing and creating a constructor for it? Maybe something like

(defn ->svg-props [size type & [bells? whistles?]]
  {:size size
   :type type
   :bells? (some? bells?)
   :whistles? (some? whistles?)})

(defn add-svg-style [{:keys [type bells? whistles?] :as svg-props}]
  (let [color (type->color type)]
    (assoc svg-props :style (merge {:fill color
                                :stroke :darkgrey}
                               (when-not bells? {:fill-opacity 0.25})
                               (when whistles? {::stroke-width 6})))))

(defn some-svg-string-3 [svg-props]
  (-> svg-props add-svg-style svg-hiccup dd-string-render))

(comment

  (some-svg-string-3 (->svg-props "size" "type"))

  )
So the description of what's getting passed around is described by the functions that create the data, and the rendering things don't really need to tell you what they take? So rather than merging the two into a single function call, you have some data manipulation functions and some render functions?

pez14:04:09

I like, @U0P0TMEFJ! Will try to internalize that pattern.

👍 1
vemv14:04:58

Curiously, clojure-lsp is doing the wrong thing there as it doesn't comply with the runtime reality

user=> (defn ^{:arglists '([{:keys [size type bells? whistles?]}])} some-svg-string-1 [params])
#'user/some-svg-string-1
user=> (-> #'some-svg-string-1 meta :arglists)
([params])
alter-meta! does, of course, what it promises.

pez14:04:01

I’m not quite following, but let me try.

jpmonettas14:04:28

but adding ^{:arglists ...} doesn't change the meta

jpmonettas14:04:44

it is overwriten

vemv14:04:10

Yep. If clojure-lsp understands it, it might suffice to call it a day. But from a tooling maker's perspective, it's not optimal to foster things that work in some environments but not others IMO, clojure-lsp could optionally use runtime info to gather any metadata mutated from the outside.

1
vemv14:04:48

Worth noting that one can use alter-meta for many other purposes, e.g. adding useful info to third-party functions

ericdallo14:04:11

clojure-lsp uses static analysis, so if you are telling to consider those arglists via a meta, it will use it instead of params when providing its features

pez14:04:37

After evaluating the function definition I get

:arglists ([props])
In the metadata of the var. Is that what you mean is a problem, @U45T93RA6?

vemv14:04:13

Yes, since the repl itself, or cider, or arbitrary other tools will be unable to access the custom info

vemv14:04:07

> clojure-lsp uses static analysis Yep, and that's great. I'd find it even greater if there was a (merge static dynamic) when available 😄

pez14:04:13

Calva does exactly that merge with quite a few things, actually. I wonder if it could/should do that here.

👀 1
pez14:04:19

At least that’s the default. The user can make it be (merge dynamic static).

👍 1
ericdallo15:04:07

yah, that's the dream @U45T93RA6 but I can't see that happening easily unfortunately, (there is actually I implemented some time ago, stub, that spaws a runtime clojure to get info of code without sourcecode, but it's was pretty specific and has its performance issues), although I agree it would be the ideal, so for now it's easier if clients do their merge like Calva

vemv15:04:02

Indeed it's the dream, and as an Emacs user i quite envy Calva for being in a position of mixing and matching at will besides simple (merge static dynamic) or (merge dynamic static) choices, probably I'd dig into heuristics ("which arglists look better"), or simple observations like "which of these systems is fresh, up and running"

catjam 1
👍 1
pez15:04:00

We do the latter of these in one direction in a sense. If clojure-lsp is not running, the repl reigns supreme and vice versa if the repl is not connected but clojure-lsp is enabled. If the repl has evaluated something and “has an answer” the default is to let that be the truth. But, indeed, we could also let that be overruled if the user changes the code after it has been evaluated. Could be fun exploring this!

calva 1
yuhan19:04:57

You can't use ^{} for putting metadata on a defn, I get confused with this sometimes:

(def ^{:arglists '([This puts metadata on the var])}
  foo identity)

(defn ^{:arglists '([But not with defn! You're putting metadata on the *symbol*, not var])}
  bar [x] x)

(defn baz
  {:arglists '([Gotta do it this way instead])}
  [x] x)

(into {} (for [v [#'foo #'bar #'baz] ]
            [v (:arglists (meta v))]))
;; => {#'repro/foo ([This puts metadata on the var]),
;;     #'repro/bar ([x]),
;;     #'repro/baz ([Gotta do it this way instead])}

🙏 1
jpmonettas19:04:43

I think you can put metadata like that, just not :arglists because they get overwritten

til 1
jpmonettas19:04:36

(defn ^:bla foo [])

(-> #'foo meta :bla) ; => true

yuhan19:04:54

Oh hmm, that seems like an oversight :thinking_face:

pez11:04:00

I blogged my take aways from this amazing thread. https://blog.agical.se/en/posts/keeping-the--arglists-of-clojure-functions-dry/ Thanks y’all! 🙏 ❤️

👏 3
💯 2
yuhan13:04:43

Nice writeup! One small thing to add - since it's All Data™, you can easily factor out any commonly repeated argument specs into a shared var, eg.

(def ^:private traffic-light-props
  '{:keys [size type bells? whistles?]})

(defn traffic-light-symbol-string-arglists
  {:arglists (list [traffic-light-props])}
  [props]
  (h/html (traffic-light-symbol-hiccup props)))
Useful if you end up deciding to add / remove keys down the line :)

pez13:04:41

Thanks! That works for the repl, but clojure-lsp is less happy about it. 😃

yuhan13:04:48

ahh, that's unfortunate - I use this pattern sometimes when working with defrecord-like data with fixed keys, relying on Cider's (REPL-powered) tooltips

teodorlu14:04:10

@U0ETXRFEW this is awesome! I've had this problem myself without having a good solution. I can't wait to see if this works as nicely for Emacs + CIDER as it does for Calva.

pez14:04:40

Oh, it will, it will.

💯 1
vemv14:04:06

Yes, CIDER is definitely happy to chug whatever metadata you provide to it I've been sort of abusing that in my current project, which is Rama-based - some things kind of escape the normal expectations as for what is a "def" and its metadata So you can make synthetic Vars, and make the :file / :line go to the right place.

💯 1
teodorlu14:04:47

Awesome, thanks for the clarification, vemv! (it's a total luxury for me to be able to just ask questions to the people who make the tools I use every day, you both rock)

😊 1
❤️ 1
ericdallo15:04:48

@U0ETXRFEW good post! question: we don't support currently in clojure-lsp, but wouldn't this be even better?

(defn traffic-light-symbol-string-arglists-w-doc
  {:arglists (:arglists (meta #'traffic-light-symbol-hiccup))
   :doc (:doc (meta #'traffic-light-symbol-hiccup))}
  [props]
  (h/html (traffic-light-symbol-hiccup props)))
so we get the arglists too from meta without the need to write symbols that may be renamed/removed/added and lost track of that, WDYT? Or is there any reason for that not be a good idea?

pez15:04:20

@UKFSJSM38 Sweet! I think that would be better. It makes the sharing clearer.

ericdallo15:04:03

Cool, feel free to open a issue, it should not be hard to support that :)

🙏 1
yuhan17:04:43

Somehow this thread led to uncovering what I'm pretty sure is a compiler bug 😮 https://ask.clojure.org/index.php/13834/compiler-exception-dynamically-arglists-metadata-recursive

🤯 1
😂 1
Noah Bogart15:04:49

i have a test that isdoing a doseq over roughly 2000 maps, sorting and filtering them, and then looping over objects in the maps (roughly [idx item] (map-indexed (fn [idx item] [idx item]) (:objects abs))) and then calling is on properties of the items. kaocha's profiling plugin says it takes roughly 1-2 seconds to run, and when i first load the repl and namespace, that's how long it takes if i wrap it in a time macro. however, repeated calls (even when running the whole test suite) only take ~65ms. is something being cached that i don't understand?

p-himik15:04:58

Does any underlying functionality use memoize or anything similar?

p-himik15:04:31

With the info you've given it's impossible to tell for certain, we can only speculate. Tests themselves are not cached in any way.

Noah Bogart15:04:16

yeah, it's strange, i'm not sure

Noah Bogart15:04:12

oh you know what, i believe there is memoization in one of the calls. i completely forgot about that

Noah Bogart15:04:22

thank you for rubber ducking and giving me the necessary hint

👍 1