Good Morning!
If y'all could change any Clojure error message to improve it, what would you change?
@folcon I’d love to see specific examples of that.
Well even given the examples you've shown here: https://clojurians.slack.com/archives/C064BA6G2/p1617773331132400 Contrast that with this:
<http://foo.rs:4:5|foo.rs:4:5>: 4:11 error: cannot assign to `x` because it is borrowed [E0506]
<http://foo.rs:4|foo.rs:4> x += 1;
^~~~~~
<http://foo.rs:3:14|foo.rs:3:14>: 3:15 note: borrow of `x` occurs here
<http://foo.rs:3|foo.rs:3> let y = &x;
^
<http://foo.rs:4:5|foo.rs:4:5>: 4:11 help: run `rustc --explain E0506` to see a detailed explanation
error: aborting due to previous error
From rustI can copy the code in the error and grep my codebase for it, when you eval a code block into the repl, you don't get anything other than relative line numbers...
I don't know, more info that can be given is better?
I don't know how much of this is possible, but I thought that there was some recorded metadata about the current form that was stored which could be retrieved? (could be completely misremembering that)...
Even just better errors during eval would make things nicer for REPL experience 😃...
Sorry I'm not just throwing together a clojure example, today isn't supposed to be a coding day for me so not in the headspace for it >_<...
NP. Yeah, I think in the general case the actual form that threw the exception isn’t available to the error reporting mechanism in Clojure so that’s a bit of a non-starter.
I thought the reader recorded all line numbers of the code that it read as metadata against the functions?
So my thought was just lookup that and display it
If it was recently evaled it might be more problematic, as I'm not sure how locally evaled code is assigned a "file", but they look like generated names, so I was under the impression it would be possible to map an exception's stacktrace info to that same "file" / line number...
I don’t think the full file path is always available — and it’s probably not going to help in a lot of cases where the exception occurs in “other” code: the “Don’t know how to create ISeq from” exception comes from line 557 of clojure.lang.RT
for example.
When you say it's not available, is it a unique value per eval? Or nil? If it's unique that should be sufficient to work out what it came from? To be honest at this point I probably should be checking my assumptions about how that all works anyway 😃...
By the time the exception is caught all you have is a stacktrace with munged class names (or classpath-relative filenames) + line numbers and the exception itself could have occurred in arbitrary code, not user code — and if it was in a thread, it’s possible that there’s no user code at all in the stack trace.
There are some heuristics that can be applied to sometimes arrive at the place in user code that called whatever blew up (but if the stack includes 3rd party libraries, you can’t tell those from user code really).
Hmm, that's not quite what I meant, what I meant is if you call meta
on a var you get line info + file info. Just did this a the repl within a comment block:
(def a 1)
=> #'example.core/a
(meta #'example.core/a)
=>
{:line 21,
:column 3,
:file "/Users/folcon/Documents/Code/clojure/example/src/example/core.clj",
:name a,
:ns #object[clojure.lang.Namespace 0x65f2460b "example.core"]}
(defn mult [x] (* x x))
=> #'example.core/mult
(meta #'example.core/mult)
=>
{:arglists ([x]),
:line 23,
:column 3,
:file "/Users/folcon/Documents/Code/clojure/example/src/example/core.clj",
:name mult,
:ns #object[clojure.lang.Namespace 0x65f2460b "example.core"]}
I didn't mean use the exception directly to get that data, I was thinking whether this could be looked up when an exception occurred so we could grab the file name and line number, look it up return the actual var/symbol and use that to get the source...
But it might be a bit too complicated...There is no control over the code when an exception occurs though — only where it is caught.
You can’t get at the context of the throw
except via the Java stacktrace.
A valid point.
(this is spurred by the State of Clojure thread on r/Clojure)
Yes, interesting. I was reading the discussion too on reddit, quite animated 🙂
I will have to have a ponder
I've been experimenting with a doctored REPL locally:
seanc@DESKTOP-30ICA76:~/oss$ clj -M:dev/repl
Selected port 35617 for the Socket REPL...
Starting clojure.main as the REPL...
Clojure 1.10.3
user=> (->> [1 2 3] (group-by identity) (map #(reduce + %)))
Error printing return value (ClassCastException) at clojure.lang.Numbers/add (Numbers.java:153).
class clojure.lang.PersistentVector cannot be cast to class java.lang.Number (clojure.lang.PersistentVector is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')
(user=> @2
Execution error (ClassCastException) at user/eval271 (dev.clj:296).
class java.lang.Long cannot be cast to class java.util.concurrent.Future (java.lang.Long and java.util.concurrent.Future are in module java.base of loader 'bootstrap')
user=> (seq 2)
Execution error (IllegalArgumentException) at user/eval273 (dev.clj:1).
Don't know how to create ISeq from: java.lang.Long
user=> (1 2 3)
Execution error (ClassCastException) at user/eval275 (dev.clj:1).
class java.lang.Long cannot be cast to class clojure.lang.IFn (java.lang.Long is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'app')
user=> (filter [1 2 3] inc)
Error printing return value (IllegalArgumentException) at clojure.lang.RT/seqFrom (RT.java:557).
Don't know how to create ISeq from: clojure.core$inc
(user=>
seanc@DESKTOP-30ICA76:~/oss$ clojure -M:rebel:dev/repl
Selected port 43003 for the Socket REPL...
Starting Rebel Readline as the REPL...
[Rebel readline] Type :repl/help for online help info
dev=> (->> [1 2 3] (group-by identity) (map #(reduce + %)))
Error printing return value (ClassCastException) at clojure.lang.Numbers/add (Numbers.java:153).
Expected a number, found value of class clojure.lang.PersistentVector
dev=> @2
Execution error (ClassCastException) at dev/eval5971 (dev.clj:296).
Expected something derefable, found value of class java.lang.Long
dev=> (seq 2)
Execution error (IllegalArgumentException) at dev/eval5973 (dev.clj:1).
Expected sequence, found: java.lang.Long
dev=> (1 2 3)
Execution error (ClassCastException) at dev/eval5975 (dev.clj:1).
Expected function, found value of class java.lang.Long
dev=> (filter [1 2 3] inc)
Error printing return value (IllegalArgumentException) at clojure.lang.RT/seqFrom (RT.java:557).
Expected sequence, found: clojure.core$inc
dev=>
Is that better or not?(this is literally just a regex replace on some messages -- I'm trying to gauge whether these are worth tackling at source, if possible)
Certainly the removal of the (clojure.lang.PersistentVector is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')
is a boon. I've taught myself to just filter that out on the trace for it never showed anything of real value to me
I'm still consider myself a beginner in the language, and all this about "loader 'app' and loader 'bootstrap'" is like eh?
The tidy-ups you've got are a lot better in helping me understand what I've done wrong.
TBH, I've gotten to the point where I translate "X cannot be cast to Y" into "expected Y, found X" anyway but I gather a lot of people find the "cannot cast" part to be confusing...
definitely, esp anyone who has zero java/jvm background
Good point.
I got started with Java back in '97 so it's hard for me to know what is obvious and what is intimidating...
The one that also is raised a lot, which I see sometimes at learner sessions (I think I saw also at clojurebridge too) is this old chestnut:
user=> (defn foo [x] x)
#'user/foo
user=> (foo 1 2)
Execution error (ArityException) at user/eval24625 (REPL:1).
Wrong number of args (2) passed to: user/foo
OK, I don't understand what is surprising / confusing about that?
It would make sooo much more sense if it was "Wrong number of args passed to : user/foo. Expected 1 got 2"
Not sure if that would be rejected on perf grounds?
Really? OK. Good to know.
Beginner's mind 🙂
I'm going to try to track down some of these at source and submit patches to try and get these improved in 1.11
I'm going to ponder on some more. I was writing some code yesterday that produced some stack (my errors), so will reproduce to see if there is anything obvious that would have helped me understand better the problems.
Thanks!
There was some really good too-and-fro in the reddit discussion. Yes, some charged, but at the same time, it shows a level of passion for the language and community.
👋
mawning
morning
moin moin
Morning
Morning
man, it's so easy to buy on amazon
click click buy
Especially for digital things
🙂
I would happily buy from alternatives, but it seems to me, there are none.
But yes, it is very easy.
I've managed to not buy anything from amazon in over a year. I guess there's always the "not buy it" alternative :P
I'm using AWS now and it feels a bit dirty TBH.
I'm curious, for anyone using clojurescript, what components do you use to make your site pretty, aka bootstrap, but clojurescript aware? I'm not totally aware of the clojure ecosystem, do I don't know what people use
Have used https://github.com/priornix/antizer
ta
Meetup starting now: https://www.meetup.com/Bristol-Clojurians/events/hnctgsyccgbkb/