hey @cgrand, I wonder what do you think about having special print-limits settings for exceptions, particulary print-length... cider lets you set print-length and print-level for exceptions, I'm not entirely sure print-level is useful, but I can see people wanting to customize exception's print-length (I do it in my cider config)
also imo, string-length at 80 might be a bit short for exceptions, simple exception messages would get elided, like this one:
#unrepl/string ["java.lang.RuntimeException: Unable to resolve symbol: x in this context, compili" #unrepl/... {:get (unrepl.replG__16476/fetch :G__17055)}
👍 on the default
what would be a better default in your mind?
I think for exceptions I would go for unrestricted string-length... print-length would mostly affect the trace vector, so I think it would be cool to make it customizable
I've seen ex-info
s with ridiculous strings in it (where you pr-str
your entire state)
in general, i think the ideal would be to have exception print-limits separated from the rest
but maybe that's too niche
I think I tend to agree with you that it should be unlimited for exceptions
I agree with the premises: exceptions suck in unrepl
but exceptions are already bad on their own
Could we take a step back and consider how to make exceptions less bad
I lean towards, as a first step totally reversing exceptions: * display them bottom up * display them inside out (most wrapped exception first)
so that the line at the bottom is the closest to what occured and where
elide wrapped-by
(the reverse of the parent exception relationship), elide previous frames, print ex-info
when present
right now, afaik, ex-info
always comes from unrepl.repl/blame-ex
, right? it's always something like: "Exception during foo phase"
that should be removed by the client
and display something only when it’s not during eval
i agree, I'm actually doing that
Expression computed succesfully but failed to print
I like that
Unreadable input
Sad unicorn
(like sad mac, for when it comes from unrepl)
but since the phase is already part of the message payload, couldn't we just take that ex-info out?
I mean, from this part:
:via
[{:type clojure.lang.ExceptionInfo,
:message "Exception during eval phase.",
:data {:unrepl.replG__16476/ex #error {#unrepl/... nil #unrepl/... {:get (unrepl.replG__16476/fetch :G__17054)}},
:unrepl.replG__16476/phase :eval},
:at [clojure.core$ex_info invokeStatic "core.clj" 4725]}
{:type clojure.lang.Compiler$CompilerException,
:message #unrepl/string ["java.lang.RuntimeException: Unable to resolve symbol: x in this context, compili" #unrepl/... {:get (unrepl.replG__16476/fetch :G__17055)}],
:at [clojure.lang.Compiler analyze "Compiler.java" 6720]}
{:type java.lang.RuntimeException,
:message "Unable to resolve symbol: x in this context",
:at [clojure.lang.Util runtimeException "Util.java" 221]}],
(this is not what I was asking at the beginning of this, but I'm also curious)
what I'm doing right now is just ommiting the first element in that vector, since I'm using the :phase
to insert a exception title
@volrath: thanks, I forgot an x
...
perfect 🙂
so ok, let me get back to what you were saying before and ask you some (probably noob) questions
when you're saying "display the exceptions bottom up", are you talking client-wise or protocol-wise?
client-wise it’s going to be roundtrip-painful (unless you get the whole exception at once)
right
well bottom-up client side is ok, inside-out not
can you explain the difference? not sure if I follow... right now I don't know how to get the wrapping information from the exception message payload. Not sure if it's because that information is not there or because I am missing something
ok I just realized that’s already what Throwable->map
does
wrapping information is the :via
vector
cool
But Throwable->map
doesn't return the wrapping inside out, does it?
what I'm doing right now is reverse that vector
Btw I've seen that exceptions get returned in different shapes on clojure 1.8 vs 1.9
I guess as long as Throwable->map
returns the same in both, we're good
couldn't figure this out while running on CI: https://clojurians.slack.com/archives/C03S1KBA2/p1511870643000121
@pesterhazy I think ./clj is the official answer to this
@dominicm is that bundled with clojure?
official instructions for doing things manually would be appreciated
https://mvnrepository.com/artifact/org.clojure/clojure/1.9.0-RC1
0.1.143 for spec.alpha
core.specs.alpha 0.1.24
is there any effort to port tools deps to CLJS?
@cgrand none that I know of
@cgrand, oh the spec dep is right there on the mvn site 🤦
yeah but mvn central was down for me
https://github.com/cgrand/packed-printer/blob/master/README.md
v0.2.0 of packed printer with a cleaner implementation and works in CLJS too
I pushed to npm too
@cgrand notice after resolving the first elision there are a couple of misalignments? I'm trying to pad the file name and lineno using the max length of what i can see in the :trace
vector, but since it is elided, I cannot render it well
but anyway, I think it's good enough for now
@volrath nice. since 1.8/direct linking every stack frame is doubled because of invoke+invokeStatic, would be cool to elide adjacent frames
this is really really cool.
Nice, an intersting problem is how to deal with misalignment
cool, thanks for the input! tbh I don't know much about this, could you point me to some info?
rerendering is a pain in the ass...
@cgrand you never got to tell me your thoughts on managing print limits for exceptions separately
@volrath wider strings, yes
higher/customizable *print-length*
? that's the one I think could help the most
why? for the alignment?
and for the elisions..
clicking the buttons when getting an exception can get old really fast
in the end, I think we should let the user decide what's best for their workflow
one setting for everything does not work
basically pre-clojure 1.8 functions got compiled into a java class that implemented a single method invoke()
Now with this direct-linking mode (which improves performance) the invoke() which is an instance method, calls down to an invokeStatic() which is a static class method. This results in longer stack frames
if you see a foo.invoke() that immediately calls foo.invokeStatic() in a stack trace, one of them should disappear
even though I do believe that customization is the key of happiness, that's not my point... stacktraces are consumed by people in different ways
I'm not saying a different print-limits management for every data type or anything like that
but I feel this is something people should be able to customize. as we know, exceptions/stacktraces are not the best thing in clojure, and unless we find the holygrail on how to deal with them (doubt it), i think we should give people the ability to customize
got it, I'll look into it, thanks!
one set of settings repl wide won’t cut
one set of settings per print context (eval, ex, log etc.) is awkward
I'll update the issue I created for settings with this and with the other problem we discussed the other day, about being able to edit settings from aux that also affect user (so that we don't have to ever use unrepl/do in user)
you need contextual display... but you probably need the same data in the different contexts
*underlying data
I have been too lazy to implement this for a while, but translating "Well-Known Signatures" in stacktraces to user-friendly displays. For example, an exception while realizing a lazy seq always contains LazySeq.sval() and LazySeq.seq() adjacently.
It couple be potentially nice to translate that into something like "Lazy Sequence"
or "Lazy Sequence Realization"
There are a lot of landmark function calls in clojure.lang.RT and clojure.lang.* that we could rewrite
@ghadi I’ve also been thinking about using frequencies of stack frames to drive elision.
@volrath I’m wondering if setting print limits each time on aux.
@cgrand what do you mean?
Sounds a little like “what about ditching the function row in favor of a touch bar?”