Fork me on GitHub
#clojure-spec
<
2017-12-12
>
athos00:12:54

As you might already see it on the Clojure ML, I just released Pinpointer, a clojure.spec error reporter similar to Expound, Inspectable, etc https://github.com/athos/Pinpointer

athos00:12:14

Pinpointer formats (and even colorizes!) spec errors in a human-readable way. The difference between Expound and Pinpointer is very subtle from the outside perspective, but Pinpointer is based on a systematic error analysis rather than heuristic approaches, and this makes it possible to report more complicated errors, such that s/conformer would transform part of the input value.

athos00:12:43

Have a look at it if you're interested, and any feedback is welcome 😉

stathissideris10:12:41

@athos I tried pinpointer and it gave up on my complex error, it just used explain as a fallback. Under what circumstances does it do that?

athos11:12:29

Thank you for giving a try to Pinpointer, @stathissideris! 😆 By default, Pinpointer falls back to s/explain if something bad happens during the error analysis. That mechanism exists to prevent Pinpointer itself from ruining the original spec error info.

athos11:12:33

If you would like to turn off the fallback behavior, call pinpoint with the option {:fallback-on-error false} like (pinpoint <spec> <input> {:fallback-on-error false})

stathissideris11:12:49

ok 🙂 in my case I have a pretty loose spec that describes a DSL, so if an expression is wrong, the errors include a lot or different options

stathissideris11:12:23

because the actual error is one of many things that can go wrong

stathissideris11:12:36

alright, I’ll try again with this option and see what happens

rickmoynihan12:12:17

can pinpointer/expound/etc… handle exceptions with spec errors in them?

rickmoynihan12:12:35

or would I need to hook into the REPLs exception printer?

deg12:12:16

In a re-frame CLJS app, my app-db holds entries created by multiple parts of my system. I want to check total validity at start of each re-frame event. So, I have (s/def ::db (s/merge :ns1/db-keys :ns2/db-keys ,,,)) in a central db.cljs file and I validate it in each event. Most of these parts are small and can be reasonably each held by a single file holding its specs, subs, and events. But, this creates a problem. The events need to require the namespace of the merged spec, while that namespace needs the namespaces of each part. What is the cleanest way to tastefully break this namespace circularity?

rickmoynihan12:12:03

Ok… partly wanting this for exceptions integrant raises from pre-init-spec… but it looks like integrant actually calls s/explain-out to generate the message in the exception so it does indeed work

athos12:12:24

Yes, s/*explain-out* plugin mechanism should handle exceptions well. If you rather want something like clojure.repl/pst, pinpointer.core/ppt would be useful.

Andreas Liljeqvist14:12:53

What is the recommended approach for shared specs in several projects? One solution is to make the spec a separate project and then require it.

taylor15:12:50

I’ve been curious about the same. I guess the options are 1) share a project 2) serialize to some other format which obv. limits what you can do in spec.

Andreas Liljeqvist15:12:03

Probably going to use a project, on the plus side I get versioned specs

misha16:12:14

1) monorepo 2) shared lib 3) send/receive as edn (you’d need to make sure specs are cljs compatible, if sharing with ui)

jeaye19:12:43

We have a separate repo for specs shared between the client and server (edn). It also contains common functions shared between the two, defs, etc.

Andreas Liljeqvist10:12:04

Thanks for your input, quite helpfull

Andreas Liljeqvist14:12:07

But I am not completely happy with that idea

Andreas Liljeqvist15:12:21

I suspect it is the best solution, but I am worried about code reloading and such. -- I will just have to try it out

stathissideris15:12:01

@andreas862 don’t forget about the leiningen checkouts setting (if that’s what you’re using)

stathissideris15:12:18

it allows you to hack on multiple non-deployed projects in parallel

Andreas Liljeqvist15:12:49

@stathissideris ah, that is a nice feature!

Andreas Liljeqvist15:12:36

Perhaps I should save my future self a lot of time and read the documentation...

johanatan21:12:48

for putting a spec on a def'd var, is the standard way to have a fspec on a self-invoking function?

johanatan21:12:56

is there some other way?

bfabry21:12:12

not sure I understand what "putting a spec on a def'd var" means. I'm guessing the answer isn't just "use fdef"?

bfabry21:12:31

or do you mean a var that's defined to be something other than a function? if so then specing it isn't supported

bfabry21:12:38

or... isn't useful

johanatan21:12:06

yea, like a var that's bound to the result of a function

bfabry21:12:14

yeah you can't spec that

johanatan21:12:18

e.g., (def ^:private keys-validators (events-schema->keys-validators events-schema))

johanatan21:12:47

or

(def ^:private refinements
  (merge user-defined-refinements normalized-base-refinements))

bfabry21:12:27

spec is meant for validating ranges of possibilities, a def'd var is "permanent" in clojure land. but yeah putting your right hand side inside a function and then fdefing that would make the most sense to me

johanatan21:12:59

mm, ok. i'll try that. i think last time i tried it couldn't get it to work with anonymous functions (and wasn't really excited about polluting the global namespace with another defn)

johanatan21:12:06

do you know if it is possible to fspec an anonymous function?

bfabry21:12:23

I mean seeing as this is all only happening once you could just do (def foo (s/assert ::spec (your code here)))

johanatan21:12:51

hmm, except that it would probably need a doto so that the result is returned?

bfabry21:12:45

assert returns the result if the result was valid

johanatan21:12:59

yea, that works. thx!

bfabry21:12:02

no worries

hlolli21:12:40

How does one do or in spec, I've tried googling

(s/explain (s/or :k1 (s/coll-of ::ar ::ar)
                 :k2 (s/coll-of ::ar ::kr)) [(AudioSignal. 1) (ControlSignal. 1)])
I also don't need these keys, but this function is probably wrong. Im makeing a transpiler to a language that has it's own dispatch functions, and since I'm automatically generating the metadata, I end up with different possibilites. Essentally, I'd want to be able to say; this vector can be a sequence of specs ::x ::y or sequence of specs ::foo ::bar.

hlolli21:12:19

troughout the ocean of functions I want to spec, they can be tuple/octet or whatever number of possibilites...

Alex Miller (Clojure team)21:12:59

probably want to look at the regex specs (cat * + ? alt)

hlolli21:12:05

ok, will look there. Lot to take in when first stepping into spec 🙂

Alex Miller (Clojure team)21:12:20

if I read what you want to do there for example you could say (s/cat :x ::ar :y (s/alt :a ::ar :k ::kr))

hlolli21:12:14

cat defines a sequence?

Alex Miller (Clojure team)21:12:21

to match sequential collections that start with ar and then have ar or kr. x, y, a, and k are all made up keys that affect the conformed result

hlolli21:12:03

ok nice, this is actually for the input parameters so it's importent, I'm tempted to do this check at the very beginning of my functions.

Alex Miller (Clojure team)21:12:25

they are most commonly used in s/fdef to spec args to functions

Alex Miller (Clojure team)21:12:36

so that is a good match

hlolli21:12:08

yes I may end up doing that, it's just this instrument function that I'm skeptical of, but I should not be. I want immediate error in runtime on wrong spec, or no matching patterns of a provided arguments, it's kinda pattern matching, therefore looking at or

Alex Miller (Clojure team)21:12:54

s/assert might be useful too

hlolli21:12:30

On extra question since I'm rambling, is there a way to have any effect on the error message In: [1] val: #object[csound.core.ControlSignal] fails spec: :csound.core/ar at: [:k1 :0] predicate: audio-signal? I'm in clojurescript and I know that the object has keys which could help the user locate his problem, as this could be within a nested ast.

taylor21:12:33

s/explain-data gives you a structure you can get useful stuff out of, and there are some libs for transforming that into something that could be useful for you

hlolli21:12:33

ah yes! in fact the object itself in question.

hlolli21:12:57

Or explain has it too, just didn't consider takeing advantage of that and throw my own error message, suited to help the user find the spec mismatch.

bfay20:12:25

pssst @U0CAUAKCG whatcha doing with csound? Looks cool

hlolli20:12:49

Who's asking @U0MF00YRX? I'm working on some interoperability between clojurescript and csound, mainly transpiler, that would be a good start, already been live-coding with this combination for few years

bfay20:12:19

Oh awesome @U0CAUAKCG! I'm trying to do some similar stuff with clojurescript/supercollider. I tried using Overtone for a bit, but found that the startup time is too painful, especially when I tried running on a Raspberry Pi

hlolli20:12:15

Yes, Overtone is bit over engineerd for Raspberry Pi. But it's way better software than Sonic Pi in my opinion.

bfay20:12:17

I think Sam Aaron had a different vision for both. The vision for Sonic Pi is about live-coding and education, it's a fun way to teach newcomers how to code. But yeah possibilities are pretty limited compared to Overtone and plain SuperCollider