expound

2019-03-22T11:30:13.001100Z

hmm, I am trying to use expound with instrument in the browser, by setting cljs.spec.alpha/*explain-out*, and the printer never seems to be called

2019-03-22T11:30:59.001700Z

I see uncaught exceptinos in the console, eg:

2019-03-22T11:31:26.002400Z

i added this:

(set! s/*explain-out* (fn [& args]
                        (prn :expound-printer! args)
                        (apply expound/printer args)))
and that fn is never called

2019-03-22T11:31:46.002800Z

is there some other setup needed?

borkdude 2019-03-22T11:36:05.003200Z

I have this in my init function:

(set! s/*explain-out* expound/printer)

2019-03-22T11:47:03.004100Z

so, something like this should work?:

(set! s/*explain-out* expound/printer)

(defn add [x y] (+ x y))

(s/fdef add :args (s/coll-of number?))

(st/instrument `add)

(add "hello" "there")

2019-03-22T11:47:26.004600Z

is fdef supposed to call explain when it throws an exception?

2019-03-22T11:47:36.004900Z

or does one catch exceptions and handle them manually somehow

borkdude 2019-03-22T11:49:24.005200Z

I think that should work yes.

borkdude 2019-03-22T11:50:24.005500Z

are you calling this from your REPL or from app code?

2019-03-22T11:55:38.005700Z

it’s in app code

2019-03-22T11:56:05.006100Z

i’ve never actually got this working so i am not sure what the expected behaviour is

2019-03-22T11:56:23.006700Z

is a nice string supposed to print to the console, or be contained somewhere in the exception that is thrown?

borkdude 2019-03-22T11:56:34.006900Z

printed to console

borkdude 2019-03-22T11:56:47.007100Z

I think…

borkdude 2019-03-22T11:56:59.007300Z

let me try in my own app

borkdude 2019-03-22T11:58:41.007500Z

hah, mine also doesn’t print anymore

borkdude 2019-03-22T11:58:50.007700Z

what cljs version and expound version?

2019-03-22T12:00:55.008100Z

clj 1.10.0, cljs 1.10.520, expound 0.7.2

borkdude 2019-03-22T12:01:27.008300Z

same here

borkdude 2019-03-22T12:07:35.008800Z

well, according to this matrix it should in theory work: https://github.com/bhb/expound/blob/master/doc/compatibility.md

borkdude 2019-03-22T12:08:57.009400Z

I’ll be following here to see if Ben has a solution. I didn’t even know my own printer was broken 🙂

borkdude 2019-03-22T12:09:58.009800Z

it does seem to be configured correctly when I type cljs.spec.alpha._STAR_explain_out_STAR_ in the browser console

2019-03-22T12:12:54.011300Z

i wonder exactly where/when the message is supposed to be produced

2019-03-22T12:13:07.011600Z

ie. where the call to explain-out was moved

bbrinck 2019-03-22T12:14:14.012700Z

The call to explain out is now in the code that handles errors in the REPL

bbrinck 2019-03-22T12:15:17.014Z

This was a change that occurred in the recent changes to how Clojure(script) handles errors

bbrinck 2019-03-22T12:16:08.015200Z

If you throw a spec error in the REPL, the error handler will call explain-out. But outside of the REPL there is no such handler.

borkdude 2019-03-22T12:16:40.015700Z

so the tooling that handles hot reloading should do this ..?

borkdude 2019-03-22T12:16:52.015900Z

not sure…

borkdude 2019-03-22T12:17:19.016700Z

but this is quite useless to me, since I don’t even use a REPL for my browser dev

borkdude 2019-03-22T12:18:25.017Z

do you know the reason why this was done?

bbrinck 2019-03-22T12:21:55.018900Z

As to why it was done, I won’t be able to give an accurate summary of https://dev.clojure.org/jira/browse/CLJ-2373 , but AIUI, the gist was that they wanted errors to contain data, leave printing to the edges

bbrinck 2019-03-22T12:22:02.019100Z

edges of the system

bbrinck 2019-03-22T12:22:46.020400Z

that means that in a CLJS app, I would expect some layer (the app, or the reloader, etc) to catch errors and print them with a configured printer … but that’s quite vague I realize 🙂 . I don’t have psuedocode handy

borkdude 2019-03-22T12:22:49.020700Z

ok. I think it’s possible to install some global error handler which does this. I mean, sentry is also able to do this, so it must be possible 🙂

bbrinck 2019-03-22T12:23:10.021200Z

Yeah, in principle, it should be doable, but I guess global error handlers are tricky in JS from my underestanding

bbrinck 2019-03-22T12:23:16.021400Z

I wonder how ghostwheel does it?

bbrinck 2019-03-22T12:23:52.022200Z

The other option is essentially to patch the way errors are thrown to put the string back in the error message itself. I don’t know if orchestra has an option for this

bbrinck 2019-03-22T12:24:06.022600Z

but basically you could change instrument to say “no, don’t include the raw data, include the formatted string”

borkdude 2019-03-22T12:24:27.022900Z

window.onerror probably 🙂

borkdude 2019-03-22T12:24:55.023100Z

I don’t like patching vanilla spec

bbrinck 2019-03-22T12:26:01.023600Z

FWIW, here is the code in the REPL that calls explain-out https://github.com/clojure/clojurescript/blob/4dfe78719bee33d44a058d9549ab98cde9e0b8fb/src/main/cljs/cljs/repl.cljs#L223

1
bbrinck 2019-03-22T12:26:39.024200Z

Oh, haha, I missed this. Yes, exactly this 🙂

borkdude 2019-03-22T12:26:44.024500Z

cool. I don’t have time right now, but it’s worth finding out what can be done in a user app and document this some time

bbrinck 2019-03-22T12:27:45.025700Z

Totally agree. I should add this to the expound documentation because it’s not obvious. Global handler examples for both Clojure and Clojurescript would be useful, but in the meantime, at least explaining why the behavior seems odd would be good

bbrinck 2019-03-22T12:28:40.026300Z

@mhuebert Would it be possible to wrap this code in a try/catch?

bbrinck 2019-03-22T12:28:51.026800Z

I realize that’s not always viable, depending on where this code is called

2019-03-22T12:28:57.027100Z

(set! s/*explain-out* expound/printer)

(defn error-handler [message url line column e]
  (js/console.error e)
  (print (repl/error->str e))
  true)

(j/assoc! js/window :onerror error-handler)

👍 1
2019-03-22T12:29:25.027600Z

^this appears to work for uncaught errors

2019-03-22T12:29:40.028Z

ie. set the global window.onerror callback to something that wraps the repl utility

2019-03-22T12:30:01.028600Z

that link to the repl code was useful

borkdude 2019-03-22T12:30:43.029100Z

This already does something:

(js/window.addEventListener "error" (fn [ev] (println "message" (.-message ev))))
but I’m getting "Uncaught #error {…}" as a string which isn’t very helpful yet 🙂

👍 1
borkdude 2019-03-22T12:31:34.029700Z

back to work now… thanks for now

bbrinck 2019-03-22T12:32:08.030700Z

If you want to create an issue in expound to update the docs at least and include whatever snippet works for you (and which browser you use), I can include some ideas in the FAQ

2019-03-22T12:32:16.030900Z

only issue i have is that shadow’s reload has its own try/catch, separate issue to figure out

bbrinck 2019-03-22T12:33:12.032500Z

Yeah, I imagine setting a global handler is a bit tricky to do reliably across browsers so I might just include some snippets instead of trying to include handler in expound. We’ll see

2019-03-22T12:33:45.032900Z

k i will make an issue

2019-03-22T12:38:38.033200Z

i wonder if cljs-devtools already prints errors using those utils

2019-03-22T12:51:10.033500Z

one could also

(extend-type ExceptionInfo
  IPrintWithWriter
  (-pr-writer [o writer opts]
    (-write writer (repl/error->str o))))

2019-03-22T12:52:12.034500Z

then if you are catching errors yourself, printing them would always use the repl utils. though maybe sometimes you want the default representation.

2019-03-22T13:06:13.034700Z

https://github.com/bhb/expound/issues/152

💯 1
2019-03-22T13:23:53.035100Z

a custom formatter seems like the way to go

2019-03-22T13:24:20.035700Z

i didn’t realize how easy they are to set up, i thought they required using a chrome extension

2019-03-22T13:24:38.035900Z

(ns app.core
  (:require [cljs.repl :as repl]))

(def devtools-error-formatter
  "Uses cljs.repl utilities to format ExceptionInfo objects in Chrome devtools console."
  #js{:header
      (fn [object config]
        (when (instance? ExceptionInfo object)
          (let [message (some->> (repl/error->str object)
                                 (re-find #"[^\n]+"))]
            #js["span" message])))
      :hasBody (constantly true)
      :body (fn [object config]
              #js["div" (repl/error->str object)])})
(defonce _
         (some-> js/window.devtoolsFormatters
                 (.unshift devtools-error-formatter)))

bbrinck 2019-03-22T14:18:52.036600Z

Awesome, thanks for looking into this!!