Fork me on GitHub
#expound
<
2021-01-09
>
Carlo16:01:30

hey! I'm unsure on how to integrate expound in a cljs project: I added [expound "0.8.7"] to my shadow-cljs.edn, and then I changed the init! function to:

(defn ^:export init! []
  (set! s/*explain-out* expound/printer)
  (mount-root))
that said, when a spec fails, I still get the usual error in the emacs minibuffer. No expound magic! Can you help me?

bbrinck17:01:22

@meditans Is it possible to test this without emacs? Does this reproduce if you just call the function in a shadow-cljs REPL on the command line?

bbrinck17:01:52

(I haven’t used shadow-cljs, so I’m not familiar with the tooling)

Carlo17:01:03

ok, so, the library works if I do something like (expound/expound string? 1)

bbrinck17:01:25

What if you instrument a function and then call it with invalid parameters?

Carlo17:01:35

it prints the output that I would expect; it's just that I don't know what to do to make my program aware

bbrinck17:01:36

(In the command-line REPL)

Carlo17:01:43

let's see

bbrinck17:01:27

In particular, CIDER can do several things with errors - it can print them to the REPL in emacs, or it can open up a CIDER specific error buffer. It should be possible to see Expound errors in the former, but I don’t know how to adjust the latter.

bbrinck17:01:15

That’s because the code in your REPL doesn’t impact how CIDER prints spec errors - that’s specific to CIDER itself, which doesn’t know about expound

Carlo17:01:32

give me a couple of minutes, I'm still learning clojure and I'm unsure on how to do a couple of things; googling

👍 3
bbrinck17:01:10

No rush. My reason I’m asking these questions 1. A command-line REPL is simplest. This will prove we are actually getting everything set up correct e.g. init! is being called as expected 2. If that works, we can then connect to a REPL in CIDER and try typing everything out in REPL, see if that works 3. Finally we can try evaluating commands in CIDER within the actual code buffer, and see what happens.

Carlo17:01:47

ok here's what I did:

(defn add1 [x] (+ 1 x))
(s/fdef add1 :args number?)
(stest/instrument `add1)

;; now (add1 nil) gives the normal error

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

;; (add1 nil) gives still the normal error

Carlo17:01:30

ok, that was done in a repl emacs opened for me. Is there a preferred way of calling a plain repl?

bbrinck17:01:12

Where is the normal error printed? In the REPL buffer?

Carlo17:01:02

that buffer is called cider-repl clojure/...

bbrinck17:01:29

Interesting! Here’s the same repro in a basic terminal REPL started with clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.773"} expound {:mvn/version "0.8.7"}}}' -e cljs.main -re node

(require '[clojure.spec.test.alpha :as stest])
(require '[clojure.spec.alpha :as s])
(require '[expound.alpha :as expound])

(defn add1 [x] (+ 1 x))
(s/fdef add1 :args number?)
(stest/instrument `add1)
(set! s/*explain-out* expound/printer)
(add1 nil)

Carlo17:01:43

thank you let me try that

bbrinck17:01:47

That prints:

Execution error - invalid arguments to cljs.user/add1 at (<cljs repl>:1).
<filename missing>:<line number missing>

-- Spec failed --------------------

Function arguments

  (nil)

should satisfy

  number?

-------------------------
Detected 1 error

bbrinck17:01:36

which leads me to believe the next step is trying to figure out a CLJS REPL in shadow and see if it’s different (my REPL above is not managed by shadow, just so we can compare)

Carlo17:01:46

hmm here's what I get:

clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.773"} expound {:mvn/version "0.8.7"}}}' -e cljs.main -re node
DEPRECATED: Libs must be qualified, change expound => expound/expound 
WARNING: When invoking clojure.main, use -M
Syntax error (ClassNotFoundException) compiling at (REPL:0:0).
cljs.main

Full report at:
/tmp/clojure-485995736718039585.edn

bbrinck17:01:08

ah, sorry, my CLJ is probably a bit old.

bbrinck17:01:22

clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.773"} expound/expound {:mvn/version "0.8.7"}}}' -e cljs.main -re node

bbrinck17:01:27

does that work?

Carlo17:01:08

no, but a somehow smaller message:

WARNING: When invoking clojure.main, use -M
Syntax error (ClassNotFoundException) compiling at (REPL:0:0).
cljs.main

Full report at:
/tmp/clojure-3622056146492718952.edn

Carlo17:01:23

so no deprecation, and no warning

bbrinck17:01:48

Well shoot. Let me upgrade my version of clj

bbrinck17:01:04

clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.773"} expound {:mvn/version "0.8.7"}}}' -M -m cljs.main -re node might work, but upgrading is taking a second

Carlo17:01:35

yeah, that works

Carlo17:01:48

is the only difference expound vs expound/expound?

bbrinck17:01:56

and I added “-M”

Carlo17:01:39

I see, to "supply main opts" I imagine

Carlo17:01:45

let me complete the test

Carlo17:01:01

yes that prints everything in the format expected by expound

Carlo17:01:14

ClojureScript 1.10.773
cljs.user=> (require '[clojure.spec.test.alpha :as stest])
nil
cljs.user=> (require '[clojure.spec.alpha :as s])
nil
cljs.user=> (require '[expound.alpha :as expound])
nil
cljs.user=> (defn add1 [x] (+ 1 x))
#'cljs.user/add1
cljs.user=> (s/fdef add1 :args number?)
cljs.user/add1
cljs.user=> (stest/instrument `add1)
[cljs.user/add1]
cljs.user=> (set! s/*explain-out* expound/printer)
#object[expound$alpha$printer]
cljs.user=> (add1 nil)
Execution error - invalid arguments to cljs.user/add1 at (<cljs repl>:1).
<filename missing>:<line number missing>

-- Spec failed --------------------

Function arguments

  (nil)

should satisfy

  number?

-------------------------
Detected 1 error

bbrinck17:01:30

Hm, so I think the next question is “does it work with shadow-cljs without emacs?”

Carlo17:01:54

which means, how do I open a shadow repl? Let me try googling

bbrinck17:01:03

It looks like shadow can start a REPL with shadow-cljs cljs-repl app or shadow-cljs node-repl but I’m going to need to install Shadow to check

Carlo17:01:46

I don't seem to have shadow-cljs globally installed, let's see

bbrinck17:01:34

Hm, me either. And it’s not clear to me how shadow-cljs will pick up dependencies in that case. Maybe that’s a bad idea

bbrinck17:01:48

Are you using deps.edn or lein for your project?

Carlo17:01:52

probably because I created a template using lein

Carlo17:01:20

so to create the template I used lein, but my deps are in a file called shadow-cljs.edn

bbrinck17:01:25

ah, gotcha

bbrinck17:01:00

Ah, OK, that makes sense. I will make a shadow-cljs.edn file now

Carlo17:01:03

here's the command I used to create the project if it helps lein new reagent-frontend my-example +shadow-cljs

bbrinck17:01:13

OK, here’s what I did

bbrinck17:01:41

yarn shadow-cljs node-repl

bbrinck17:01:58

(and expound is in my dependencies in shadow-cljs.edn)

bbrinck17:01:20

then I ran the sequence above and I did not get the expound output

Carlo17:01:48

interesting! Let me try, just for good measure, although I'm sure I won't get that either

Carlo17:01:09

but anyway, why would that happen?

bbrinck17:01:15

In fact, it looks like shadow is printing the raw error object

bbrinck17:01:24

#error {:message "Call to #'cljs.user/add1 did not conform to spec.", :data {:cljs.spec.alpha/problems [{:path [], :pred cljs.core/number?, :val (nil), :via [], :in []}], :cljs.spec.alpha/spec #object[cljs.spec.alpha.t_cljs$spec$alpha30168], :cljs.spec.alpha/value (nil), :cljs.spec.alpha/fn cljs.user/add1, :cljs.spec.alpha/args (nil), :cljs.spec.alpha/failure :instrument}}

Carlo17:01:27

the one that begins with #error

Carlo17:01:34

yeah, that's what gets printed in emacs also

bbrinck17:01:58

Ah, cool, this is helpful. So that’s not even the old error message - note that is different from what you get without shadow

Carlo17:01:19

that would be Execution error - invalid arguments to cljs.user/add1 at (<cljs repl>:1).?

bbrinck17:01:33

Note that if you modify my other REPL (without expound) and you don’t call (set! s/*explain-out* expound/printer), you get the following

bbrinck17:01:43

Execution error - invalid arguments to cljs.user/add1 at (<cljs repl>:1).
(nil) - failed: number?

bbrinck17:01:47

ha, yep, what you said 🙂

bbrinck17:01:11

so anyway, my guess is that Shadow has it’s REPL configured to handle errors differently

bbrinck17:01:45

I’m googling now on how to adjust it

Carlo17:01:56

awesome 🙂

bbrinck17:01:55

Hm, I’m coming up empty so far. Basically if shadow lets you configure the REPL, we want to take that #error object and send the data to expound

Carlo17:01:58

I'm also googling, let's see what we can find; maybe it would be worthwhile to just ask in #shadow-cljs?

bbrinck18:01:00

Yeah, I think maybe #shadow-cljs would know more. I’m super new to shadow

Carlo18:01:02

:repl-pprint ?

Carlo18:01:52

do you want me to ask or do you want to do that yourself? (I have the feeling you understand better what this is about)

bbrinck18:01:26

Feel free to ask - I unfortunately need to leave in a few minutes

bbrinck18:01:57

But the general idea is that you want to turn that error into a string by calling expound/printer with the data. You can get inspiration from what CLJS does on the REPL: https://github.com/clojure/clojurescript/blob/5e88d3383e0f950c4de410d3d6ee11769f3714f4/src/main/cljs/cljs/repl.cljs#L218-L222

Carlo18:01:49

I'll do, thank you for all the help! 🙂

bbrinck18:01:02

Good luck!

🙏 3
bbrinck18:01:45

If you figure it out, let me know here or in Github and I’ll add a section to the readme for Shadow

Carlo18:01:05

I will, thanks!

Carlo21:01:46

no progress yet, but I opened an issue here https://github.com/thheller/shadow-cljs/issues/825