Fork me on GitHub

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


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.


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


@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?


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.


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})


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


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


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


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


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


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?


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


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.


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


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


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


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


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...


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


is there some other way?


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


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


or... isn't useful


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


yeah you can't spec that


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



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


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


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)


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


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


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


assert returns the result if the result was valid


yea, that works. thx!


no worries


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.


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)


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))


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


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


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


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.


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


ah yes! in fact the object itself in question.


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.


pssst @U0CAUAKCG whatcha doing with csound? Looks cool


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


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


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


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