This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-03-05
Channels
- # announcements (1)
- # asami (1)
- # babashka (7)
- # beginners (11)
- # biff (2)
- # calva (7)
- # cider (1)
- # clara (5)
- # clj-kondo (223)
- # clojure (83)
- # clojure-boston (3)
- # clojure-conj (1)
- # clojure-europe (9)
- # clojure-uk (1)
- # clojurescript (11)
- # cursive (76)
- # emacs (30)
- # figwheel-main (2)
- # fulcro (13)
- # hyperfiddle (6)
- # lsp (6)
- # malli (2)
- # nyc (4)
- # off-topic (78)
- # practicalli (9)
- # reitit (4)
- # sci (20)
- # shadow-cljs (49)
- # sql (9)
- # xtdb (5)
In R, if I pass a variable named foo (e.g. with a value of 1) to a function f as an argument, it is possible to know not only the value (i.e. 1) from within the function, but also, that the name of the variable was "foo" before entering the function. Is this possible in Clojure?
Out of curiosity, why do you need this @U0CDMAKD0 ? Unless you're building some kind of a tool it sounds like a weird thing to do. Are you sure passing keyword arguments/a map doesn't solve your problem?
After using Clojure a lot and R a decent amount, I feel like I can say with reasonable confidence that, while R has a lot of nice affordances for quick'n'dirty hacking of something, those affordances should be avoided in any piece of code that's used for serious work. Time and time, and time again, have I seen scientists use R with confidence only to discover that they had been using something not exactly right. They were saving time and effort, but it was actually an exchange for correctness. When your affordances are a trade-off with correctness, they should not be used. To the actual question - it's possible if it's not a function that you're calling but a macro. But macros have their downsides as well, and relying on the names of the passed arguments can trivially become a trade-off with correctness. Keyword arguments or an arg map might not be the most convenient to use, but they don't have the above issues.
When the provenance of a value is important, there are a few idiomatic Clojure techniques. Provenance is data and we would separate it from the accident or convenience of naming of a variable. One technique is to pass 2 arguments. Another technique is to attach the provenance to the data value with metadata.
@UEQPKG7HQ I find it useful in logs of output to know the name of the variable that was passed to a function, especially in interesting cases where the function does something funky. I use it a lot in error handling.
@U0HG4EHMH, that's true I could just pass in a label (every function call). That's not as elegant, but accomplishes the same thing.
@U2FRKM4TW, how do you define serious work?
Your workflow sounds quite unorthodox; I have even more questions now! 😅 Printing the stack trace when something is weird is much more informative imho (because it shows you the entire path that led to the execution of that piece of code) and it's actually used very heavily both in error handling and is/can be tightly integrated in pretty much every modern editor/IDE (e.g. each point in the stack is a link to that line in the code) What more does inspecting the argument names in the caller function offer you?
If you are programming in R and you get a stack trace, something has gone very wrong
Roughly the question was: "R can do this, can Clojure do this?". The answer appears to be "no, Clojure cannot". The rest is interesting but tangential 🙂
What more does inspecting the argument names in the caller function offer you? It offers context.
R and Clojure are designed very differently. It's not always accurate to assume that they have to use the same tools to solve similar problems.
Also, suppose you write a function and put it into your own library. A colleague at work uses your function out of convenience. Your function may call a host of other functions. Your colleague benefits by getting a message with a variable name they recognize (because they wrote it).
Your problem seems to be provenance but having functions know what their attributes were called before they entered the context of the function is just one solution.
thanks for your thoughts, @UEQPKG7HQ
> how do you define serious work? @U0CDMAKD0 Anything that needs to be reliable and predictable when it comes to a developer's work. E.g. this particular feature - if you rely on it just for tracing, that's totally fine (although I fail to see how it's better than stacktraces that you can get without throwing exceptions). But someone can rely on what is passed to a function to determine how the function should behave. It automatically makes the code brittle because then any refactoring will be non-trivial.
> Your colleague benefits by getting a message with a variable name they recognize (because they wrote it). Or by seeing the stacktrace that mentions the exact location. :)
I wasn't saying that R is not used there. I was saying that it provides quite a few things that shouldn't be used in such work. To an extent, it's true in Clojure as well - both have the ability to import all names from a package without having to use an alias or an FQN. But in Clojure it's much, much less prevalent.
I suspect we're misunderstanding your problem @U0CDMAKD0
@U2FRKM4TW, I have learned to read stacktraces as well as another person, but I can tell you that many people new to Clojure complain about them 😉 . That said, the original question I asked has been answered.
Oh, they are cryptic! Of course. But they do have that information, and they are pretty clear when you get to know them closer.
The use case is not "let's learn to read stacktraces", the use case is can Clojure do X. Thanks
The stack trace is just a list of locations in the code, error messages are a different story
Sorry, that came off as condescending. I just meant that it doesn't have to be cryptic
I'm not insinuating that you don't know something, I was simply having a debate on why something is useful.
agree - but whether it is useful or not is a different discussion. Suffice it to be "is it possible". It is not possible.
Just in case you find it useful, you could just print the first few lines of the stack trace and the person who reads that will understand that this particular function was called by ns X line N, which was in turn called by ns Y line K etc (My point was that even if Clojure allowed you to do what you're asking for, I wouldn't recommend it because it sounds wrong.) Anyway, I'm personally very susceptible to the XY problem and I was just trying to make sure this question wasn't another example of that.
I understand what you are saying. Sometimes, there will not be an error per se, it depends on the search space
Yeah, stack traces aren't just for errors
Can you give an example in R where what you describe is useful but where having a warning message with a stack trace of that place is not useful?
I don't quite follow it. If there is a warning message (I assume that's what you need argument names for?), then you know what to "trap" - what to check for.
@U2FRKM4TW - it might be an edge case of an equation
In your example how would that colleague know where to look for the variable names (if this was R)?
Where would that "message with a variable name" end up?
The way I understand that example, when I call (f x y z)
, you're saying that I would benefit more from seeing x
, y
, z
in a warning message rather than seeing the exact location and chain of calls where that f
was called.
Is that correct?
Colleague writes code using library X. They often don't know how library X does what it needs to do. They do know the names of the variables in their code.
yes, exactly @U2FRKM4TW
I call (f x y z)
10s of times in my code - how would seeing x
, y
, z
help me?
When I see a stacktrace and notice that it has lines that point to my code, I just pick the latest line and see exactly everything about that call site - including argument names.
You can even make it automatic! By parsing the stacktrace and printing the corresponding source code lines.
x
is just a symbol. Seeing x
tells me exactly nothing.
I would really like to understand the use case, but I struggle.
Are there things where a stacktrace is not enough? Where it provides less information?
it may tell you nothing, but many business people use names that are meaningful to them. Instead of x, think "sales", or "clicks", or whatever
> parsing the stacktrace and printing the corresponding source code lines. @U0CDMAKD0 This would work, no?
(let [sales (get-sales)]
(report sales)
(let [sales (increase sales)]
(report sales)))
Assuming that there was only one warning issued by the above code, how would knowing sales
help me, given that I don't know which of the report
functions raised a particular warning?if your expertise was in knowing the sales of your business, this context would be useful in remembering why this might be an issue
Given that you can get a full stacktrace, process it, extract the names from it, do anything else - all without having to interrupt the program by raising an exception or in any other way - I'd say that Clojure actually does have that capability. :)
I appreciate your point of view. I have to go now. Thanks again for all your responses
Just as an example of what I meant:
(require '[clojure.string :as str])
(import '( PrintWriter StringWriter))
(defn report-issue* [ex]
(let [sw (StringWriter.)
pw (PrintWriter. sw)]
(.printStackTrace ex pw)
;; Or more likely `log/warning`.
(println (->> (str/split-lines (str sw))
(take 5)
(str/join \newline)))))
(defmacro report-issue [msg data]
`(let [ex# (ex-info ~msg ~data)]
(report-issue* ex#)))
(defn report [sales]
(when-not (contains? sales :amount)
(report-issue "No amount in sales" {:sales sales}))
(println "Current sales:" (:amount sales 0)))
(let [sales {}]
(report sales)
(let [sales (update sales :amount (fnil + 0) 10)]
(report sales)))
That would result in
clojure.lang.ExceptionInfo: No amount in sales {:sales {}}
at dev$report.invokeStatic(scratch_1.clj:19)
at dev$report.invoke(scratch_1.clj:17)
at dev$eval104039.invokeStatic(scratch_1.clj:24)
at dev$eval104039.invoke(scratch_1.clj:23)
Current sales: 0
Current sales: 10
Hi, anyone knows how to format this piece of code?
#datalog.parser.type.Query{:qfind #datalog.parser.type.FindTuple{:elements [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Variable{:symbol ?fname}]}, :qwith nil, :qin [#datalog.parser.type.BindScalar{:variable #datalog.parser.type.SrcVar{:symbol $}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?fname}} #datalog.parser.type.BindScalar{:variable #datalog.parser.type.Variable{:symbol ?lname}}], :qwhere [#datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/firstName} #datalog.parser.type.Variable{:symbol ?fname}]} #datalog.parser.type.Pattern{:source #datalog.parser.type.DefaultSrc{}, :pattern [#datalog.parser.type.Variable{:symbol ?e} #datalog.parser.type.Constant{:value :user/lastName} #datalog.parser.type.Variable{:symbol ?lname}]}], :qlimit nil, :qoffset nil, :qreturnmaps #datalog.parser.type.ReturnMaps{:mapping-type :keys, :mapping-keys (#datalog.parser.type.MappingKey{:mapping-key foo})}}
it's https://github.com/replikativ/datalog-parser/blob/4bafbcd3720fdaa090907290479b5f84228b7822/test/datalog/parser_test.cljcHave you tried binding https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/*print-namespace-maps*?
If you mean those #symbol
things - they aren't namespaces, they are tagged literals.
Reading that as data will create a record of the tagged type.
Maybe zprint
library will help here but not sure.
ok, thanks. and right, now I am looking at it and it is obviously not namespaced but tagged literals, thanks