What people want from Clojure error messages

296 views
Skip to first unread message

Michael Klishin

unread,
Jan 24, 2013, 2:27:48 PM1/24/13
to clo...@googlegroups.com
There is a discussion about Clojure error messages and how
specifically they can be improved on clojure-dev:

Because poor error messages primarily trip newcomers to the language,
I am a bit surprised to see this issue discussed on the closed
mailing list said beginners cannot join [quickly or at all].

So, if you have something specific to say on the topic, say it here.
--
MK

http://github.com/michaelklishin
http://twitter.com/michaelklishin

AtKaaZ

unread,
Jan 24, 2013, 2:37:18 PM1/24/13
to clo...@googlegroups.com
Hi.
There are a bunch of calls to Util.runtimeException("msgHere") which can be replaced with Util.runtimeException("msgHere", e); where e is the exception just caught (aka the cause), for example here:
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L4570

and here's a case where this particular thing would've maybe been more useful:
https://groups.google.com/d/msg/clojure/MIKccMX9gvk/ULv4AjqdNQEJ
but granted that isn't much more helpful in this particular case, but I see no reason why to not include the cause exception like that.


--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
 
 



--
I may be wrong or incomplete.
Please express any corrections / additions,
they are encouraged and appreciated.
At least one entity is bound to be transformed if you do ;)

Rich Morin

unread,
Jan 24, 2013, 3:08:04 PM1/24/13
to clo...@googlegroups.com
On Jan 24, 2013, at 11:27, Michael Klishin wrote:
> So, if you have something specific to say on the topic, say it here.

Well, these comments more general in nature; hope that's OK...

I find Clojure's default error messages to be noisy, ugly, and hard to
decipher. What I'd like is a short, clear explanation of the problem
and a way to get the details, if desired.


Ben Schneiderman gives a lot of good advice on error messages in

"Designing the User Interface"
http://www.amazon.com/dp/0201694972

The second edition of the book includes this table (8.1):

Error-Message Guidelines

* Be as specific and precise as possible.

* Be constructive: indicate what needs to be done.

* Use a positive tone: avoid condemnation.

* Choose user-centered phrasing.

* Consider multiple levels of messages.

* Keep consistent grammatical form, terminology,
and abbreviations.

* Keep consistent visual form and placement.

The book (economically available from Amazon) covers each point
in detail. This might provide useful criteria for any rework.

-r

--
http://www.cfcl.com/rdm Rich Morin
http://www.cfcl.com/rdm/resume r...@cfcl.com
http://www.cfcl.com/rdm/weblog +1 650-873-7841

Software system design, development, and documentation


vemv

unread,
Jan 24, 2013, 3:38:28 PM1/24/13
to clo...@googlegroups.com
repl-y (Lein's default repl) only shows the first line of the stack trace rather than the whole thing. nrepl.el can optionally behave the same.

It's a good default - as I see it, it relieves 90% of the pain associated to errors in clojure.

As for the contents of those first-lines - it often is unrelated to the actual cause of the error - but determining it is absolutely non-trivial. That's why Java displays the whole traces, all lines can be necessary.

Not checking the validity of every single possible parameter a fn receives, or every single interaction that can occur between pieces of code, is just a fact of life when programming in dynamic languages.

Finally, what's a good language, error-info wise? Can't recall one.

AtKaaZ

unread,
Jan 24, 2013, 4:41:39 PM1/24/13
to clo...@googlegroups.com
Here are some more: (which I'm getting from my old gist from here https://gist.github.com/3895312 after I recheck those and tell you only the ones relevant to this subject)

1. This one has to do with namespace expected format for :import
https://groups.google.com/d/msg/clojure/JCwpbqbrHUE/m1Fx13Ye6JkJ
that still persists with clojure {:major 1, :minor 5, :incremental 0, :qualifier "RC2"} and lein 2.
What I didn't state there was that the project.clj needs one or both of the following lines:
:main dood.core
:aot :all
else lein compile won't compile those and they only trigger on lein run (if :main is present) or lein repl
that is to have the top-level forms executed (thanks xeqi)

In addition to that, if you use this line instead:
 (:import '(nl.bitwalker.useragentutils.UserAgent))

you get this error:
c:\1\dood>lein compile
Compiling dood.core
Exception in thread "main" java.lang.ClassNotFoundException: quote.(nl.bitwalker
.useragentutils.UserAgent), compiling:(core.clj:1)
        at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3387)
        at clojure.lang.Compiler.compile1(Compiler.java:7035)
        at clojure.lang.Compiler.compile1(Compiler.java:7025)
        at clojure.lang.Compiler.compile(Compiler.java:7097)
        at clojure.lang.RT.compile(RT.java:387)
        at clojure.lang.RT.load(RT.java:427)
        at clojure.lang.RT.load(RT.java:400)
        at clojure.core$load$fn__4890.invoke(core.clj:5415)
        at clojure.core$load.doInvoke(core.clj:5414)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invoke(core.clj:5227)
        at clojure.core$compile$fn__4895.invoke(core.clj:5426)
        at clojure.core$compile.invoke(core.clj:5425)
        at user$eval7.invoke(NO_SOURCE_FILE:1)
        at clojure.lang.Compiler.eval(Compiler.java:6511)
        at clojure.lang.Compiler.eval(Compiler.java:6501)
        at clojure.lang.Compiler.eval(Compiler.java:6477)
        at clojure.core$eval.invoke(core.clj:2797)
        at clojure.main$eval_opt.invoke(main.clj:297)
        at clojure.main$initialize.invoke(main.clj:316)
        at clojure.main$null_opt.invoke(main.clj:349)
        at clojure.main$main.doInvoke(main.clj:427)
        at clojure.lang.RestFn.invoke(RestFn.java:421)
        at clojure.lang.Var.invoke(Var.java:419)
        at clojure.lang.AFn.applyToHelper(AFn.java:163)
        at clojure.lang.Var.applyTo(Var.java:532)
        at clojure.main.main(main.java:37)
Caused by: java.lang.ClassNotFoundException: quote.(nl.bitwalker.useragentutils.
UserAgent)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at clojure.lang.DynamicClassLoader.findClass(DynamicClassLoader.java:61)

        at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:186)
        at dood.core$loading__4784__auto__.invoke(core.clj:1)
        at clojure.lang.AFn.applyToHelper(AFn.java:159)
        at clojure.lang.AFn.applyTo(AFn.java:151)
        at clojure.lang.Compiler$InvokeExpr.eval(Compiler.java:3382)
        ... 26 more
Compilation failed: Subprocess failed

Which is actually much closer to the real error you want to see, when compared to the other two errors.
 Because it's telling me that's expecting a class in the place of that '(...) form ie. not a list/vector.

-----------------------------------
2.
"
dorun clojure.core

Argument Lists:
[coll]
[n coll]

Documentation:
When lazy sequences are produced via functions that have side
  effects, any effects other than those needed to produce the first
  element in the seq do not occur until the seq is consumed.
   dorun can
  be used to force any effects. Walks through the successive nexts
   of
  the seq, does not retain the head and returns nil.
"
,(dorun (map println '(1)) nil) ; no error on this one
nil
;the following are ok:
,(dorun (map println '(1)))
1
,(dorun 12 (map println '(1 2 3)))
1
2
3
=> ,(dorun 1 (map println '(1 2 3)))
1
2
nil
=> ,(dorun 0 (map println '(1 2 3)))
1
nil

-----------------------------------
3.
,(quote 1 2 3)
1
Ignores 2 3, without any notifications.
same thing with "var" instead of "quote"
=> ,(var defn for 3)
#'clojure.core/defn

-----------------------------------
4. files that have UTF BOM at the beginning yield the following error
c:\1\dood>lein compile
Compiling dood.core
Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol:
 ? in this context, compiling:(dood/core.clj:1)
        at clojure.lang.Compiler.analyze(Compiler.java:6281)
        at clojure.lang.Compiler.analyze(Compiler.java:6223)
        at clojure.lang.Compiler.compile1(Compiler.java:7030)
        at clojure.lang.Compiler.compile(Compiler.java:7097)
        at clojure.lang.RT.compile(RT.java:387)
        at clojure.lang.RT.load(RT.java:427)
        at clojure.lang.RT.load(RT.java:400)
        at clojure.core$load$fn__4890.invoke(core.clj:5415)
        at clojure.core$load.doInvoke(core.clj:5414)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.core$load_one.invoke(core.clj:5227)
        at clojure.core$compile$fn__4895.invoke(core.clj:5426)
        at clojure.core$compile.invoke(core.clj:5425)
        at user$eval7.invoke(NO_SOURCE_FILE:1)
        at clojure.lang.Compiler.eval(Compiler.java:6511)
        at clojure.lang.Compiler.eval(Compiler.java:6501)
        at clojure.lang.Compiler.eval(Compiler.java:6477)
        at clojure.core$eval.invoke(core.clj:2797)
        at clojure.main$eval_opt.invoke(main.clj:297)
        at clojure.main$initialize.invoke(main.clj:316)
        at clojure.main$null_opt.invoke(main.clj:349)
        at clojure.main$main.doInvoke(main.clj:427)
        at clojure.lang.RestFn.invoke(RestFn.java:421)
        at clojure.lang.Var.invoke(Var.java:419)
        at clojure.lang.AFn.applyToHelper(AFn.java:163)
        at clojure.lang.Var.applyTo(Var.java:532)
        at clojure.main.main(main.java:37)
Caused by: java.lang.RuntimeException: Unable to resolve symbol: ? in this conte
xt
        at clojure.lang.Util.runtimeException(Util.java:170)
        at clojure.lang.Compiler.resolveIn(Compiler.java:6766)
        at clojure.lang.Compiler.resolve(Compiler.java:6710)
        at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6671)
        at clojure.lang.Compiler.analyze(Compiler.java:6244)
        ... 26 more
Compilation failed: Subprocess failed


That's all I got for now, other stuff from my aforementioned gist do not apply to this subject, although I believe it's idiomatic clojure coding which leads to such cryptic error messages and hard to track down bugs ie. when not passing the right form/parameters. I think it's idiomatic clojure coding to not check for all possible variants that could happen (but keep in mind that I haven't coded anything in clojure and i'm a noob). But having more checks is probably not much fun to code and some people don't like to add code to protect the user(dev) from itself not to mention this would incur some kind of performance penalty also.


On Thu, Jan 24, 2013 at 8:27 PM, Michael Klishin <michael....@gmail.com> wrote:

--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
 
 

brad bowman

unread,
Jan 24, 2013, 7:40:56 PM1/24/13
to clo...@googlegroups.com
On Friday, January 25, 2013 6:27:48 AM UTC+11, Michael Klishin wrote:
[..]
Because poor error messages primarily trip newcomers to the language,
I am a bit surprised to see this issue discussed on the closed
mailing list said beginners cannot join [quickly or at all].

So, if you have something specific to say on the topic, say it here.


As a Clojure beginner with no Java experience, I find the error messages to often
be very Java-centric, usually needing my best-guess interpretation into the
Clojure in front of me (nil and NullPointer being the only specific I can recall right now).
I'll collect any more I come across, especially since I'm sure I can generate weirder,
"why would you even type that?" code than people who have already learned Clojure.
It looks like the Java-ness was touched on in the clojure-dev discussion also.

When Perl, after 15 or so years, finally included the variable name in "uninitialized"
warnings it was truly a blessed day.  Perl also has a "use diagnositics" pragma that
nicely decrypts and decorates error messages:
  perl -Mdiagnostics -Mstrict -E 'my $x; print $x'
This opt-in feature is newbie-friendly without burdening more experienced users.

Brad

Brian Marick

unread,
Jan 24, 2013, 7:52:27 PM1/24/13
to clo...@googlegroups.com

A typo in an identifier should not lead to the following. Although I understand that `lein repl` trims stack traces, that should be the default, rather than having every substantial user-facing program trim `clojure.lang.Compiler` (etc.) messages for the sake of its users.

> Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol: midje-position-stringx in this context, compiling:(midje/emission/plugins/default.clj:34)
> at clojure.lang.Compiler.analyze(Compiler.java:6281)
> at clojure.lang.Compiler.analyze(Compiler.java:6223)
> at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3497)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6457)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyze(Compiler.java:6223)
> at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3548)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6457)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyze(Compiler.java:6223)
> at clojure.lang.Compiler$IfExpr$Parser.parse(Compiler.java:2650)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyze(Compiler.java:6223)
> at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5618)
> at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:5919)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyze(Compiler.java:6223)
> at clojure.lang.Compiler$IfExpr$Parser.parse(Compiler.java:2650)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyze(Compiler.java:6223)
> at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5618)
> at clojure.lang.Compiler$LetExpr$Parser.parse(Compiler.java:5919)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyze(Compiler.java:6223)
> at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3548)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6457)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyze(Compiler.java:6223)
> at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3548)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6457)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyze(Compiler.java:6223)
> at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3548)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6457)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyze(Compiler.java:6223)
> at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5618)
> at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5054)
> at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3674)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6453)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6443)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.access$100(Compiler.java:37)
> at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:518)
> at clojure.lang.Compiler.analyzeSeq(Compiler.java:6455)
> at clojure.lang.Compiler.analyze(Compiler.java:6262)
> at clojure.lang.Compiler.analyze(Compiler.java:6223)
> at clojure.lang.Compiler.eval(Compiler.java:6515)
> at clojure.lang.Compiler.load(Compiler.java:6952)
> at clojure.lang.RT.loadResourceScript(RT.java:359)
> at clojure.lang.RT.loadResourceScript(RT.java:350)
> at clojure.lang.RT.load(RT.java:429)
> at clojure.lang.RT.load(RT.java:400)
> at clojure.core$load$fn__4890.invoke(core.clj:5415)
> at clojure.core$load.doInvoke(core.clj:5414)
> at clojure.lang.RestFn.invoke(RestFn.java:408)
> at clojure.core$load_one.invoke(core.clj:5227)
> at clojure.core$load_lib.doInvoke(core.clj:5264)
> at clojure.lang.RestFn.applyTo(RestFn.java:142)
> at clojure.core$apply.invoke(core.clj:603)
> at clojure.core$load_libs.doInvoke(core.clj:5298)
> at clojure.lang.RestFn.applyTo(RestFn.java:137)
> at clojure.core$apply.invoke(core.clj:603)
> at clojure.core$require.doInvoke(core.clj:5381)
> at clojure.lang.RestFn.invoke(RestFn.java:421)
> at midje.emission.api$load_plugin.invoke(api.clj:25)
> at clojure.lang.Var.invoke(Var.java:415)
> at midje.Bootstrap$bootstrap.invoke(Bootstrap.clj:17)
> at midje.repl$eval23.invoke(sweet.clj:2)
> at clojure.lang.Compiler.eval(Compiler.java:6511)
> at clojure.lang.Compiler.load(Compiler.java:6952)
> at clojure.lang.RT.loadResourceScript(RT.java:359)
> at clojure.lang.RT.loadResourceScript(RT.java:350)
> at clojure.lang.RT.load(RT.java:429)
> at clojure.lang.RT.load(RT.java:400)
> at clojure.core$load$fn__4890.invoke(core.clj:5415)
> at clojure.core$load.doInvoke(core.clj:5414)
> at clojure.lang.RestFn.invoke(RestFn.java:408)
> at clojure.core$load_one.invoke(core.clj:5227)
> at clojure.core$load_lib.doInvoke(core.clj:5264)
> at clojure.lang.RestFn.applyTo(RestFn.java:142)
> at clojure.core$apply.invoke(core.clj:603)
> at clojure.core$load_libs.doInvoke(core.clj:5298)
> at clojure.lang.RestFn.applyTo(RestFn.java:137)
> at clojure.core$apply.invoke(core.clj:603)
> at clojure.core$require.doInvoke(core.clj:5381)
> at clojure.lang.RestFn.invoke(RestFn.java:2422)
> at midje.repl$eval5$loading__4784__auto____6.invoke(repl.clj:1)
> at midje.repl$eval5.invoke(repl.clj:1)
> at clojure.lang.Compiler.eval(Compiler.java:6511)
> at clojure.lang.Compiler.eval(Compiler.java:6501)
> at clojure.lang.Compiler.load(Compiler.java:6952)
> at clojure.lang.RT.loadResourceScript(RT.java:359)
> at clojure.lang.RT.loadResourceScript(RT.java:350)
> at clojure.lang.RT.load(RT.java:429)
> at clojure.lang.RT.load(RT.java:400)
> at clojure.core$load$fn__4890.invoke(core.clj:5415)
> at clojure.core$load.doInvoke(core.clj:5414)
> at clojure.lang.RestFn.invoke(RestFn.java:408)
> at clojure.core$load_one.invoke(core.clj:5227)
> at clojure.core$load_lib.doInvoke(core.clj:5264)
> at clojure.lang.RestFn.applyTo(RestFn.java:142)
> at clojure.core$apply.invoke(core.clj:603)
> at clojure.core$load_libs.doInvoke(core.clj:5298)
> at clojure.lang.RestFn.applyTo(RestFn.java:137)
> at clojure.core$apply.invoke(core.clj:603)
> at clojure.core$require.doInvoke(core.clj:5381)
> at clojure.lang.RestFn.invoke(RestFn.java:408)
> at user$eval1.invoke(NO_SOURCE_FILE:1)
> at clojure.lang.Compiler.eval(Compiler.java:6511)
> at clojure.lang.Compiler.eval(Compiler.java:6501)
> at clojure.lang.Compiler.eval(Compiler.java:6500)
> at clojure.lang.Compiler.eval(Compiler.java:6477)
> at clojure.core$eval.invoke(core.clj:2797)
> at clojure.main$eval_opt.invoke(main.clj:297)
> at clojure.main$initialize.invoke(main.clj:316)
> at clojure.main$null_opt.invoke(main.clj:349)
> at clojure.main$main.doInvoke(main.clj:427)
> at clojure.lang.RestFn.invoke(RestFn.java:421)
> at clojure.lang.Var.invoke(Var.java:419)
> at clojure.lang.AFn.applyToHelper(AFn.java:163)
> at clojure.lang.Var.applyTo(Var.java:532)
> at clojure.main.main(main.java:37)
> Caused by: java.lang.RuntimeException: Unable to resolve symbol: midje-position-stringx in this context
> at clojure.lang.Util.runtimeException(Util.java:170)
> at clojure.lang.Compiler.resolveIn(Compiler.java:6766)
> at clojure.lang.Compiler.resolve(Compiler.java:6710)
> at clojure.lang.Compiler.analyzeSymbol(Compiler.java:6671)
> at clojure.lang.Compiler.analyze(Compiler.java:6244)
> ... 137 more
> Subprocess failed



--------
Occasional consulting on programming technique
Contract programming in Ruby and Clojure
Latest book: /Functional Programming for the Object-Oriented Programmer/
https://leanpub.com/fp-oo

Brian Marick

unread,
Jan 24, 2013, 8:03:52 PM1/24/13
to clo...@googlegroups.com
I've also noticed that the messages that come from botched macroexpansions are often not useful in that they contain no clues about the original source, and sometimes not even a useful reference to the original namespace.

I have a similar problem in Midje. When a macroexpansion blows up, I catch the exception and trim the stack trace to midje-specific frames. I display them to the user with the comment "This stack trace *might* help:". It almost never does.

Midje's design leads to nested macroexpansions. I've thought of capturing each stage and providing them in failure messages, so the user can (1) see the original form that provoked the problem, and (2) see which part of a larger form led to the problem. It would be nice if such history were somehow generally available.

I realize that I'm old-fashioned in my enthusiasm for macros and the use of dynamic scope.

Hugo Duncan

unread,
Jan 26, 2013, 9:19:31 AM1/26/13
to clo...@googlegroups.com
Brian Marick <mar...@exampler.com> writes:

> I've also noticed that the messages that come from botched
> macroexpansions are often not useful in that they contain no clues
> about the original source, and sometimes not even a useful reference
> to the original namespace.

If these are macros that are under your control, and they are generating
new forms, you may be able to improve this by explicitly propagating
:line and :file metadata from the input form onto the generated form.

Hugo
Reply all
Reply to author
Forward
0 new messages