Two suggestions re: core.spec, `ns`, and clojure 1.9alpha11

1,865 views
Skip to first unread message

Brian Marick

unread,
Aug 20, 2016, 6:17:59 AM8/20/16
to clo...@googlegroups.com
Yesterday, a bug was filed against Suchwow under 1.9alpha11. It turns out to have been a use of `ns …(require…` instead of `(ns …(:require`. Not in Suchwow, but in Midje. Unfortunately, the Suchwow file the bug report pointed at *also* had that typo - apparently I am prone to it - so adding the colon to the require there didn’t make the problem go away. 

That caused me to lose my temper and make a fool of myself, which is neither here nor there, except that I apologize to @puredanger. 

I have two suggestions, though:

1. It has long been the case that Clojure allowed `(ns (require…)` even though that’s strictly incorrect. I suggest that, for backwards compatibility, it be allowed going forward. That is, I think it does no harm for a correct `ns` statement to allow symbols as well as keywords. That wrong code in Midje has been there since Clojure 1.2. 

2. The following is not a good error message:

Exception in thread "main" java.lang.IllegalArgumentException: Call to clojure.core/ns did not conform to spec:
In: [2] val: ((require [such.vars :as var] [such.immigration :as immigrate]) (require midje.checking.checkers.defining midje.checking.checkers.chatty midje.checking.checkers.simple midje.checking.checkers.combining midje.checking.checkers.collection)) fails at: [:args] predicate: (cat :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses),  Extra input

- It would be better to say “`require` should be a keyword, not a symbol” than "fails at: [:args] predicate: (cat :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses),  Extra input”

- Suggest Clojure.spec error messages follow the “inverted pyramid” structure of news reports: https://en.wikipedia.org/wiki/Inverted_pyramid That would mean the text message about the error would come first, the spec second, and the wrong expression last. 

- It would be better to name the namespace that has the problem.

- The stack trace adds nothing to the error. If anything, it makes it less understandable, as the sheer amount of text is offputting.

My https://github.com/marick/structural-typing does (a small) part of what clojure.spec does. I went to a lot of effort to get it to produce good error messages, and it turned out OK. I doubt it would be applicable to clojure.spec, but I’d be happy to sign over any code that could be of use. 

It’s unfortunate that reporting errors is so much harder than detecting them. 

Alex Miller

unread,
Aug 20, 2016, 10:03:51 AM8/20/16
to Clojure


On Saturday, August 20, 2016 at 5:17:59 AM UTC-5, Brian Marick wrote:
Yesterday, a bug was filed against Suchwow under 1.9alpha11. It turns out to have been a use of `ns …(require…` instead of `(ns …(:require`. Not in Suchwow, but in Midje. Unfortunately, the Suchwow file the bug report pointed at *also* had that typo - apparently I am prone to it - so adding the colon to the require there didn’t make the problem go away. 

That caused me to lose my temper and make a fool of myself, which is neither here nor there, except that I apologize to @puredanger. 

I have two suggestions, though:

1. It has long been the case that Clojure allowed `(ns (require…)` even though that’s strictly incorrect. I suggest that, for backwards compatibility, it be allowed going forward. That is, I think it does no harm for a correct `ns` statement to allow symbols as well as keywords. That wrong code in Midje has been there since Clojure 1.2. 

We discussed this before releasing the specs and decided to start on the strict side. That said, this is still an alpha and there is plenty of time to change our minds prior to official release of 1.9 if that ends up being a catastrophic decision.
 

2. The following is not a good error message:

Exception in thread "main" java.lang.IllegalArgumentException: Call to clojure.core/ns did not conform to spec:
In: [2] val: ((require [such.vars :as var] [such.immigration :as immigrate]) (require midje.checking.checkers.defining midje.checking.checkers.chatty midje.checking.checkers.simple midje.checking.checkers.combining midje.checking.checkers.collection)) fails at: [:args] predicate: (cat :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses),  Extra input

You left out this next important line too since it points you to exactly the file and line where the error occurs:

, compiling:(such/sequences.clj:1:1) 

spec produces very detailed error messages driven by the specs and the value being validated. I admit that in some cases the output from a spec error (particularly for complicated syntaxes where there are wide alternative fan-outs) is daunting. However, spec error messages are going to be increasingly common for all of us to see and understand and I think it is worth taking the time to slow down and actually read them.

> Call to clojure.core/ns did not conform to spec:
              ^^^^^^^^^^^^^^ <- macro that was passed invalid values 
> In: [2] 
        ^^^ <- the data path in the :args passed to the macro, here, the 2th element is the require clause (ns = 0, such.sequences = 1)

> val: ((require [such.vars :as var] ...)
         ^^ <- the remaining part of the value that did not match (it has already matched or "consumed" the first two elements successfully)

> fails at: [:args]
               ^^^^^^ <- the path in the ns fdef spec to the failure point

> predicate: (cat :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses),  
                    ^^^etc -> the remaining part of the spec it was trying to match

> Extra input
  specs way of saying that it found something (the val above) but it wasn't what the spec expected next


I'm not trying to pretend this is as easy to digest as an error message that would be produced by hand-written validation and error code, but it's also notoriously difficult to cover all possible cases (which is why the Clojure DSLs have so many error gaps despite having a lot of that code). We are looking to decrease the amount of custom error detection and reporting, so anything we do has to be something we can do generically.

For the specific case of macroexpanded error reporting, I think there *are* more things we can do here (generically) that will improve readability. We *know* we are in the context of checking an fdef spec on a macro, so some of the ":args" stuff is maybe not necessary. Having the val and predicate for a s/cat forwarded to the point of mismatch is both great (as it's specific) but also confusing (because parts of both the input and the cat spec) are missing which removes important context. I think there are ways to indicate that's happening better. Earlier versions of this also reported the full args and we ended up removing that because in many cases it feels redundant (or is potentially large). Maybe there is some heuristic we could follow on when that would help. 
 
- It would be better to say “`require` should be a keyword, not a symbol” than "fails at: [:args] predicate: (cat :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses),  Extra input”

- Suggest Clojure.spec error messages follow the “inverted pyramid” structure of news reports: https://en.wikipedia.org/wiki/Inverted_pyramid That would mean the text message about the error would come first, the spec second, and the wrong expression last. 

- It would be better to name the namespace that has the problem.

As mentioned above, the next line that you omitted points to the file and line with the problem (I went to some trouble to ensure that was reported properly from the point of use).
 
- The stack trace adds nothing to the error. If anything, it makes it less understandable, as the sheer amount of text is offputting.

I agree, but this is to some degree an orthogonal issue. Tools (including the base REPL) choose what to do when they receive an exception from the compiler.  I would like to think more carefully about what the compiler is producing (informationally) and also how tools should be expected to handle these errors. Certainly in cases like this, the stack trace is unnecessary and that's true of many compiler errors. That is a fixable problem and I am interested in making improvements in this area still in 1.9.
 
My https://github.com/marick/structural-typing does (a small) part of what clojure.spec does. I went to a lot of effort to get it to produce good error messages, and it turned out OK. I doubt it would be applicable to clojure.spec, but I’d be happy to sign over any code that could be of use. 

Thanks, I doubt there's much we could use directly.
 
It’s unfortunate that reporting errors is so much harder than detecting them. 

Indeed. Unfortunately errors are read by people and people are hard. :) 

Brian Marick

unread,
Aug 20, 2016, 10:40:21 AM8/20/16
to clo...@googlegroups.com

On Aug 20, 2016, at 9:03 AM, Alex Miller <al...@puredanger.com> wrote:

You left out this next important line too since it points you to exactly the file and line where the error occurs:

, compiling:(such/sequences.clj:1:1) 

This is interesting. Here’s why I missed it. I attach the error message I saw from `lein midje`. Notice that the spec error appears twice, once at the top, once at the end. The line showing the source file appears only with the top one. The one at the bottom of the screen is the one I looked at. I think that’s pretty natural.

==== attach

Exception in thread "main" java.lang.IllegalArgumentException: Call to clojure.core/ns did not conform to spec:
In: [2] val: ((require [such.vars :as var] [such.immigration :as immigrate]) (require midje.checking.checkers.defining midje.checking.checkers.chatty midje.checking.checkers.simple midje.checking.checkers.combining midje.checking.checkers.collection)) fails at: [:args] predicate: (cat :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses),  Extra input
:clojure.spec/args  (midje.checkers "Checkers are for checking results of checkables, or checking \n   that appropriate arguments are passed to prerequisites" (require [such.vars :as var] [such.immigration :as immigrate]) (require midje.checking.checkers.defining midje.checking.checkers.chatty midje.checking.checkers.simple midje.checking.checkers.combining midje.checking.checkers.collection))
, compiling:(midje/checkers.clj:1:1)
at clojure.lang.Compiler.macroexpand1(Compiler.java:6795)
at clojure.lang.Compiler.macroexpand(Compiler.java:6861)
at clojure.lang.Compiler.eval(Compiler.java:6935)
at clojure.lang.Compiler.load(Compiler.java:7403)
at clojure.lang.RT.loadResourceScript(RT.java:374)
at clojure.lang.RT.loadResourceScript(RT.java:365)
at clojure.lang.RT.load(RT.java:455)
at clojure.lang.RT.load(RT.java:421)
at clojure.core$load$fn__7645.invoke(core.clj:6008)
at clojure.core$load.invokeStatic(core.clj:6007)
at clojure.core$load.doInvoke(core.clj:5991)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5812)
at clojure.core$load_one.invoke(core.clj:5807)
at clojure.core$load_lib$fn__7590.invoke(core.clj:5852)
at clojure.core$load_lib.invokeStatic(core.clj:5851)
at clojure.core$load_lib.doInvoke(core.clj:5832)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$load_libs.invokeStatic(core.clj:5889)
at clojure.core$load_libs.doInvoke(core.clj:5873)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$require.invokeStatic(core.clj:5911)
at clojure.core$require.doInvoke(core.clj:5911)
at clojure.lang.RestFn.invoke(RestFn.java:512)
at midje.parsing.3_from_lexical_maps.from_fake_maps$eval8453$loading__7531__auto____8454.invoke(from_fake_maps.clj:1)
at midje.parsing.3_from_lexical_maps.from_fake_maps$eval8453.invokeStatic(from_fake_maps.clj:1)
at midje.parsing.3_from_lexical_maps.from_fake_maps$eval8453.invoke(from_fake_maps.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6951)
at clojure.lang.Compiler.eval(Compiler.java:6940)
at clojure.lang.Compiler.load(Compiler.java:7403)
at clojure.lang.RT.loadResourceScript(RT.java:374)
at clojure.lang.RT.loadResourceScript(RT.java:365)
at clojure.lang.RT.load(RT.java:455)
at clojure.lang.RT.load(RT.java:421)
at clojure.core$load$fn__7645.invoke(core.clj:6008)
at clojure.core$load.invokeStatic(core.clj:6007)
at clojure.core$load.doInvoke(core.clj:5991)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5812)
at clojure.core$load_one.invoke(core.clj:5807)
at clojure.core$load_lib$fn__7590.invoke(core.clj:5852)
at clojure.core$load_lib.invokeStatic(core.clj:5851)
at clojure.core$load_lib.doInvoke(core.clj:5832)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$load_libs.invokeStatic(core.clj:5889)
at clojure.core$load_libs.doInvoke(core.clj:5873)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$require.invokeStatic(core.clj:5911)
at clojure.core$require.doInvoke(core.clj:5911)
at clojure.lang.RestFn.invoke(RestFn.java:551)
at midje.parsing.lexical_maps$eval8445$loading__7531__auto____8446.invoke(lexical_maps.clj:1)
at midje.parsing.lexical_maps$eval8445.invokeStatic(lexical_maps.clj:1)
at midje.parsing.lexical_maps$eval8445.invoke(lexical_maps.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6951)
at clojure.lang.Compiler.eval(Compiler.java:6940)
at clojure.lang.Compiler.load(Compiler.java:7403)
at clojure.lang.RT.loadResourceScript(RT.java:374)
at clojure.lang.RT.loadResourceScript(RT.java:365)
at clojure.lang.RT.load(RT.java:455)
at clojure.lang.RT.load(RT.java:421)
at clojure.core$load$fn__7645.invoke(core.clj:6008)
at clojure.core$load.invokeStatic(core.clj:6007)
at clojure.core$load.doInvoke(core.clj:5991)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5812)
at clojure.core$load_one.invoke(core.clj:5807)
at clojure.core$load_lib$fn__7590.invoke(core.clj:5852)
at clojure.core$load_lib.invokeStatic(core.clj:5851)
at clojure.core$load_lib.doInvoke(core.clj:5832)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$load_libs.invokeStatic(core.clj:5889)
at clojure.core$load_libs.doInvoke(core.clj:5873)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$require.invokeStatic(core.clj:5911)
at clojure.core$require.doInvoke(core.clj:5911)
at clojure.lang.RestFn.invoke(RestFn.java:512)
at midje.parsing.2_to_lexical_maps.fakes$eval8437$loading__7531__auto____8438.invoke(fakes.clj:1)
at midje.parsing.2_to_lexical_maps.fakes$eval8437.invokeStatic(fakes.clj:1)
at midje.parsing.2_to_lexical_maps.fakes$eval8437.invoke(fakes.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6951)
at clojure.lang.Compiler.eval(Compiler.java:6940)
at clojure.lang.Compiler.load(Compiler.java:7403)
at clojure.lang.RT.loadResourceScript(RT.java:374)
at clojure.lang.RT.loadResourceScript(RT.java:365)
at clojure.lang.RT.load(RT.java:455)
at clojure.lang.RT.load(RT.java:421)
at clojure.core$load$fn__7645.invoke(core.clj:6008)
at clojure.core$load.invokeStatic(core.clj:6007)
at clojure.core$load.doInvoke(core.clj:5991)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5812)
at clojure.core$load_one.invoke(core.clj:5807)
at clojure.core$load_lib$fn__7590.invoke(core.clj:5852)
at clojure.core$load_lib.invokeStatic(core.clj:5851)
at clojure.core$load_lib.doInvoke(core.clj:5832)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$load_libs.invokeStatic(core.clj:5889)
at clojure.core$load_libs.doInvoke(core.clj:5873)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$require.invokeStatic(core.clj:5911)
at clojure.core$require.doInvoke(core.clj:5911)
at clojure.lang.RestFn.invoke(RestFn.java:1523)
at midje.data.prerequisite_state$eval8244$loading__7531__auto____8245.invoke(prerequisite_state.clj:1)
at midje.data.prerequisite_state$eval8244.invokeStatic(prerequisite_state.clj:1)
at midje.data.prerequisite_state$eval8244.invoke(prerequisite_state.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6951)
at clojure.lang.Compiler.eval(Compiler.java:6940)
at clojure.lang.Compiler.load(Compiler.java:7403)
at clojure.lang.RT.loadResourceScript(RT.java:374)
at clojure.lang.RT.loadResourceScript(RT.java:365)
at clojure.lang.RT.load(RT.java:455)
at clojure.lang.RT.load(RT.java:421)
at clojure.core$load$fn__7645.invoke(core.clj:6008)
at clojure.core$load.invokeStatic(core.clj:6007)
at clojure.core$load.doInvoke(core.clj:5991)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5812)
at clojure.core$load_one.invoke(core.clj:5807)
at clojure.core$load_lib$fn__7590.invoke(core.clj:5852)
at clojure.core$load_lib.invokeStatic(core.clj:5851)
at clojure.core$load_lib.doInvoke(core.clj:5832)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$load_libs.invokeStatic(core.clj:5889)
at clojure.core$load_libs.doInvoke(core.clj:5873)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$require.invokeStatic(core.clj:5911)
at clojure.core$require.doInvoke(core.clj:5911)
at clojure.lang.RestFn.invoke(RestFn.java:805)
at midje.checking.checkables$eval8208$loading__7531__auto____8209.invoke(checkables.clj:1)
at midje.checking.checkables$eval8208.invokeStatic(checkables.clj:1)
at midje.checking.checkables$eval8208.invoke(checkables.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6951)
at clojure.lang.Compiler.eval(Compiler.java:6940)
at clojure.lang.Compiler.load(Compiler.java:7403)
at clojure.lang.RT.loadResourceScript(RT.java:374)
at clojure.lang.RT.loadResourceScript(RT.java:365)
at clojure.lang.RT.load(RT.java:455)
at clojure.lang.RT.load(RT.java:421)
at clojure.core$load$fn__7645.invoke(core.clj:6008)
at clojure.core$load.invokeStatic(core.clj:6007)
at clojure.core$load.doInvoke(core.clj:5991)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5812)
at clojure.core$load_one.invoke(core.clj:5807)
at clojure.core$load_lib$fn__7590.invoke(core.clj:5852)
at clojure.core$load_lib.invokeStatic(core.clj:5851)
at clojure.core$load_lib.doInvoke(core.clj:5832)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$load_libs.invokeStatic(core.clj:5889)
at clojure.core$load_libs.doInvoke(core.clj:5873)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$require.invokeStatic(core.clj:5911)
at clojure.core$require.doInvoke(core.clj:5911)
at clojure.lang.RestFn.invoke(RestFn.java:436)
at midje.sweet$eval7876$loading__7531__auto____7877.invoke(sweet.clj:4)
at midje.sweet$eval7876.invokeStatic(sweet.clj:4)
at midje.sweet$eval7876.invoke(sweet.clj:4)
at clojure.lang.Compiler.eval(Compiler.java:6951)
at clojure.lang.Compiler.eval(Compiler.java:6940)
at clojure.lang.Compiler.load(Compiler.java:7403)
at clojure.lang.RT.loadResourceScript(RT.java:374)
at clojure.lang.RT.loadResourceScript(RT.java:365)
at clojure.lang.RT.load(RT.java:455)
at clojure.lang.RT.load(RT.java:421)
at clojure.core$load$fn__7645.invoke(core.clj:6008)
at clojure.core$load.invokeStatic(core.clj:6007)
at clojure.core$load.doInvoke(core.clj:5991)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5812)
at clojure.core$load_one.invoke(core.clj:5807)
at clojure.core$load_lib$fn__7590.invoke(core.clj:5852)
at clojure.core$load_lib.invokeStatic(core.clj:5851)
at clojure.core$load_lib.doInvoke(core.clj:5832)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$load_libs.invokeStatic(core.clj:5889)
at clojure.core$load_libs.doInvoke(core.clj:5873)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$require.invokeStatic(core.clj:5911)
at clojure.core$require.doInvoke(core.clj:5911)
at clojure.lang.RestFn.invoke(RestFn.java:2793)
at midje.repl$eval4497$loading__7531__auto____4498.invoke(repl.clj:1)
at midje.repl$eval4497.invokeStatic(repl.clj:1)
at midje.repl$eval4497.invoke(repl.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6951)
at clojure.lang.Compiler.eval(Compiler.java:6940)
at clojure.lang.Compiler.load(Compiler.java:7403)
at clojure.lang.RT.loadResourceScript(RT.java:374)
at clojure.lang.RT.loadResourceScript(RT.java:365)
at clojure.lang.RT.load(RT.java:455)
at clojure.lang.RT.load(RT.java:421)
at clojure.core$load$fn__7645.invoke(core.clj:6008)
at clojure.core$load.invokeStatic(core.clj:6007)
at clojure.core$load.doInvoke(core.clj:5991)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5812)
at clojure.core$load_one.invoke(core.clj:5807)
at clojure.core$load_lib$fn__7590.invoke(core.clj:5852)
at clojure.core$load_lib.invokeStatic(core.clj:5851)
at clojure.core$load_lib.doInvoke(core.clj:5832)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$load_libs.invokeStatic(core.clj:5889)
at clojure.core$load_libs.doInvoke(core.clj:5873)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:659)
at clojure.core$require.invokeStatic(core.clj:5911)
at clojure.core$require.doInvoke(core.clj:5911)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at user$eval4491.invokeStatic(form-init1525926663091106112.clj:1)
at user$eval4491.invoke(form-init1525926663091106112.clj:1)
at clojure.lang.Compiler.eval(Compiler.java:6951)
at clojure.lang.Compiler.eval(Compiler.java:6941)
at clojure.lang.Compiler.eval(Compiler.java:6940)
at clojure.lang.Compiler.load(Compiler.java:7403)
at clojure.lang.Compiler.loadFile(Compiler.java:7341)
at clojure.main$load_script.invokeStatic(main.clj:276)
at clojure.main$init_opt.invokeStatic(main.clj:278)
at clojure.main$init_opt.invoke(main.clj:278)
at clojure.main$initialize.invokeStatic(main.clj:309)
at clojure.main$null_opt.invokeStatic(main.clj:343)
at clojure.main$null_opt.invoke(main.clj:340)
at clojure.main$main.invokeStatic(main.clj:422)
at clojure.main$main.doInvoke(main.clj:385)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: Call to clojure.core/ns did not conform to spec:
In: [2] val: ((require [such.vars :as var] [such.immigration :as immigrate]) (require midje.checking.checkers.defining midje.checking.checkers.chatty midje.checking.checkers.simple midje.checking.checkers.combining midje.checking.checkers.collection)) fails at: [:args] predicate: (cat :attr-map (? map?) :clauses :clojure.core.specs/ns-clauses),  Extra input
:clojure.spec/args  (midje.checkers "Checkers are for checking results of checkables, or checking \n   that appropriate arguments are passed to prerequisites" (require [such.vars :as var] [such.immigration :as immigrate]) (require midje.checking.checkers.defining midje.checking.checkers.chatty midje.checking.checkers.simple midje.checking.checkers.combining midje.checking.checkers.collection))

at clojure.spec$macroexpand_check.invokeStatic(spec.clj:627)
at clojure.spec$macroexpand_check.invoke(spec.clj:616)
at clojure.lang.AFn.applyToHelper(AFn.java:156)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clojure.lang.Var.applyTo(Var.java:700)
at clojure.lang.Compiler.macroexpand1(Compiler.java:6789)
... 239 more
Error encountered performing task 'midje' with profile(s): 'base,system,user,provided,dev,1.9'
Subprocess failed
986 $ 

Brian Marick

unread,
Aug 20, 2016, 10:58:14 AM8/20/16
to clo...@googlegroups.com
On Aug 20, 2016, at 9:03 AM, Alex Miller <al...@puredanger.com> wrote:

We discussed this before releasing the specs and decided to start on the strict side. That said, this is still an alpha and there is plenty of time to change our minds prior to official release of 1.9 if that ends up being a catastrophic decision.

I urge you to change your minds. Whose life is harmed, and how, by continuing to allow `require` as well as `:require`? 

Alex Miller

unread,
Aug 20, 2016, 3:12:00 PM8/20/16
to Clojure


On Saturday, August 20, 2016 at 9:40:21 AM UTC-5, Brian Marick wrote:

On Aug 20, 2016, at 9:03 AM, Alex Miller <al...@puredanger.com> wrote:

You left out this next important line too since it points you to exactly the file and line where the error occurs:

, compiling:(such/sequences.clj:1:1) 

This is interesting. Here’s why I missed it. I attach the error message I saw from `lein midje`. Notice that the spec error appears twice, once at the top, once at the end. The line showing the source file appears only with the top one. The one at the bottom of the screen is the one I looked at. I think that’s pretty natural.

This is what I'm talking about as far as how tools (incl the REPL) display compiler errors, so in agreement.

Alex Miller

unread,
Aug 20, 2016, 3:13:38 PM8/20/16
to Clojure
The downside is that we then have to support two similar but different ways to do the same thing forever and that adds complexity.

But your feedback is noted and we'll see how things progress before we make any decisions.


se...@corfield.org

unread,
Aug 20, 2016, 6:26:48 PM8/20/16
to clo...@googlegroups.com

I disagree (strongly) with your position here Brian. I’ll try to explain clearly why but first a little background…

 

At World Singles, we’ve always done multi-version testing against the stable version of Clojure that we plan to use in production and also against the very latest master SNAPSHOT. This gives us an early heads up when a breaking change is introduced. When the ns spec hit master, our build broke. We had three namespaces with incorrect syntax – based on the documentation and what all the books and tutorials show – so we simply fixed them (one was require, two were import). Luckily none of the libraries we rely on had similar breakages.

 

Then we moved to Alpha 11 and found another namespace with require – clearly our tests don’t have sufficient coverage (duly noted, JIRA issue to follow I expect).

 

Despite having to fix our code, we welcome the stricter compiler checking here. There are very few things in language design that frustrate me more than preserving bad compiler behavior in the name of “backward compatibility” on the grounds that “if we fix this, people who have code that we never intended to work will see breakages, and be forced to correct their bad code”. That’s what you’re asking for here: that Clojure/core preserve unintended behavior so that people who have code that works “accidentally” aren’t forced to modify their code to match what has always been intended.

 

Why do I feel so strongly about this? A few things… I built one of the first ANSI-validated C compilers which focused on “the letter of the law” as far as flagging undefined, unspecified, and implementation-defined behavior. After that, I was on the ANSI C++ Standards Committee for eight years where we had to deal with this same sort of issue over and over again in terms of deciding what unintended legacy behaviors should be enshrined as standard vs outlawed (vs pushed to one of those three behaviors). After all that standards work, I then had to deal with Macromedia / Adobe ColdFusion on and off since 2001: a product that values backward compatibility so deeply that it repeatedly does exactly what you’re asking Clojure/core to do – it won’t fix any number of bugs because they might break unintentionally working code. You can’t begin to imagine what decades of that position does to a language – it’s a horrible, inconsistent, mess of a language, full of special cases, warts, and surprising behavior. I wouldn’t wish that on any sane developer.

 

Sean Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

 

Timothy Baldridge

unread,
Aug 20, 2016, 6:43:41 PM8/20/16
to clo...@googlegroups.com
As a side note to this conversation, I hit this (require) vs (:require) thing almost a year ago when porting a file to CLJS. ClojureScript has been enforcing these keyword regulations for some time, as well as only allowing a single :require, not allowing anything but :require, :use, etc.


For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.

To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.


For more options, visit https://groups.google.com/d/optout.

 

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
“One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.”
(Robert Firth)

Brian Marick

unread,
Aug 20, 2016, 6:49:02 PM8/20/16
to clo...@googlegroups.com

On Aug 20, 2016, at 5:26 PM, se...@corfield.org wrote:

I disagree (strongly) with your position here Brian. I’ll try to explain clearly why but first a little background…

I too have felt the pain of having to maintain backward compatibility. However, I’m reminded, in this case, of Mark Twain’s “The cat, having sat upon a hot stove lid, will not sit upon a hot stove lid again. But he won't sit upon a cold stove lid, either.”

That’s why I tend to ask questions, not about abstract principles, but about what - given these particular alternatives - happens to which people?

The problem with recourse to general principles and past experience is that it’s easy to overlook that I’m suggesting a change from:

  ns-clause-header ::= keyword?

… to:

  ns-clause-header ::= keyword? || symbol?


Timothy Baldridge

unread,
Aug 20, 2016, 7:30:59 PM8/20/16
to clo...@googlegroups.com
Brian, let's make it more concrete then...why should the Clojure compiler continue to support undocumented features that make code unportable?



--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Brian Marick

unread,
Aug 20, 2016, 9:22:58 PM8/20/16
to clo...@googlegroups.com

On Aug 20, 2016, at 6:30 PM, Timothy Baldridge <tbald...@gmail.com> wrote:

Brian, let's make it more concrete then...why should the Clojure compiler continue to support undocumented features that make code unportable?

Because: 

1. People who want to port to clojurescript will incur exactly the same cost as they do now.
2. People who don’t want to port to clojurescript will incur no additional cost.
3. Clojurescript maintainers will incur no additional cost.
4. Clojure maintainers will incur the cost of adding “or symbol” to current code.
5. No one writing documentation will incur any cost, as what was not mentioned before will continue to be unmentioned.

6. There will be a psychic cost because of an undocumented inconsistency between clojure and clojurescript.
7. If, at some point, clojure and clojurescript shared code for the implementation of `ns`, one or the other would have to change the pre 1.9-alpha11 behavior.

Do I have this enumeration of costs wrong?

It’s a bit surprising to me that my explicit appeal to consider costs and benefits to real people is not being addressed.

Colin Fleming

unread,
Aug 20, 2016, 10:14:01 PM8/20/16
to clo...@googlegroups.com
With respect to preserving undocumented behaviour, while in general I'm in favour of making compilers stricter, in this case it seems like the change breaks a lot of existing code in ways that are impossible for library consumers to fix themselves - they have to wait for an update to the library, or fork it. Leaving the symbol option seems like a very low-impact change, it's not going to be a massive amount of technical debt in Clojure itself. There are many areas of unspecified behaviour in the reader (for example, keywords starting with numbers, the keyword function allowing creation of unreadable keywords etc) which have not been fixed because it would break some existing code - I suspect the impact of fixing that would be far less than the impact of this change. 

I don't understand why this particular change is so important that significant breakage to real code is considered acceptable. I agree with Brian that it doesn't seem very pragmatic. 

--

Colin Fleming

unread,
Aug 20, 2016, 10:27:36 PM8/20/16
to clo...@googlegroups.com
I think there's considerable scope to produce better error messages automatically than what spec produces, and I hope that can happen for 1.9. The error message produced by the code I demoed at the conj last year would be:

Unexpected symbol 'require' at <exact error location> while parsing namespace clauses. Expected :refer-clojure, :require, :use, :import, :load or :gen-class. 

The only thing the grammar developer needs to do to get that error is to provide a descriptive name of "namespace clauses" for the main alternation. If they don't do that, the error message would just say "...while parsing clojure.core/ns..." instead, which is still acceptable.

There is plenty of prior art on how to do this, it's not a "people are hard" problem. I think the precise error messages you want for validating data structures are not optimal for code error messages. I know the mantra is that code is data, but in this case I believe that a specialised implementation for code is superior. But I realise that ship has sailed.

--

Andrew Oberstar

unread,
Aug 20, 2016, 10:38:26 PM8/20/16
to clo...@googlegroups.com
What about a compromise where you could opt-in or opt-out of checking macro specs at compile time (via a compiler option)? It seems worth preserving the correctness of the spec, without forcing all of the breakage.

Andrew Oberstar

On Sat, Aug 20, 2016 at 9:13 PM Colin Fleming <colin.ma...@gmail.com> wrote:
With respect to preserving undocumented behaviour, while in general I'm in favour of making compilers stricter, in this case it seems like the change breaks a lot of existing code in ways that are impossible for library consumers to fix themselves - they have to wait for an update to the library, or fork it. Leaving the symbol option seems like a very low-impact change, it's not going to be a massive amount of technical debt in Clojure itself. There are many areas of unspecified behaviour in the reader (for example, keywords starting with numbers, the keyword function allowing creation of unreadable keywords etc) which have not been fixed because it would break some existing code - I suspect the impact of fixing that would be far less than the impact of this change. 

I don't understand why this particular change is so important that significant breakage to real code is considered acceptable. I agree with Brian that it doesn't seem very pragmatic. 
On 21 August 2016 at 13:22, Brian Marick <mar...@roundingpegs.com> wrote:

On Aug 20, 2016, at 6:30 PM, Timothy Baldridge <tbald...@gmail.com> wrote:

Brian, let's make it more concrete then...why should the Clojure compiler continue to support undocumented features that make code unportable?

Because: 

1. People who want to port to clojurescript will incur exactly the same cost as they do now.
2. People who don’t want to port to clojurescript will incur no additional cost.
3. Clojurescript maintainers will incur no additional cost.
4. Clojure maintainers will incur the cost of adding “or symbol” to current code.
5. No one writing documentation will incur any cost, as what was not mentioned before will continue to be unmentioned.

6. There will be a psychic cost because of an undocumented inconsistency between clojure and clojurescript.
7. If, at some point, clojure and clojurescript shared code for the implementation of `ns`, one or the other would have to change the pre 1.9-alpha11 behavior.

Do I have this enumeration of costs wrong?

It’s a bit surprising to me that my explicit appeal to consider costs and benefits to real people is not being addressed.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

Sean Corfield

unread,
Aug 20, 2016, 10:41:43 PM8/20/16
to Clojure Mailing List
On 8/20/16, 7:13 PM, "Colin Fleming" <clo...@googlegroups.com on behalf of colin.ma...@gmail.com> wrote:
> in this case it seems like the change breaks a lot of existing code

I disagree. Compared to the vast amount of Clojure code out there, I would contend that this breaks very little code – and that code is arguably wrong in the first place. Most of the handful of library maintainers that have been notified about this increase in strictness have been quick to fix their code (and mostly have been quick to release new versions). This has also been my experience so far for libraries that defined their own versions of one or more of the new predicates added in Clojure 1.9.0 – very quick updates to add the appropriate :exclude to :refer-clojure in those namespaces (and that was for a _warning_, not even an error!).

> they have to wait for an update to the library, or fork it.

Or stay on Clojure 1.8.0. Which is true of any other change in Clojure itself that causes breakage in code.

I find it very interesting that, in the past we’ve often see relatively slow take up of the prerelease builds, with folks saying they don’t want to use prerelease software, yet for Clojure 1.9.0 we’re seeing much more uptake of clojure.spec driving early adoption of these builds.

Sean



Sean Corfield

unread,
Aug 20, 2016, 10:51:13 PM8/20/16
to Clojure Mailing List
Or keep the stricter compiler and:

1. People who want to port to clojurescript will incur exactly the same cost as they do now.

**2. People who don’t want to port to clojurescript and don’t want to move to Clojure 1.9 will incur no additional cost.

3. Clojurescript maintainers will incur no additional cost.

**4. COST REMOVED: Clojure maintainers will incur NO additional cost.

5. No one writing documentation will incur any cost, as what was not mentioned before will continue to be unmentioned.

**6. COST REMOVED: There will be NO psychic cost because there will NOT be an undocumented inconsistency between clojure and clojurescript.

**7. COST REMOVED: If, at some point, clojure and clojurescript shared code for the implementation of `ns`, neither of them would have to change how they interpret the ns form

**8. BENEFIT ADDED: We benefit because an undocumented and unintended behavior went away, and one of the most confusing and complex Clojure forms becomes more consistent – we no longer have to explain to beginners that trip over (require …) mysteriously working inside ns, contrary to all the documentation out there, that “Yeah, we know (require …) works in ns, like it does outside ns, but it’s not supposed to and you shouldn’t do that – it’s a bug in Clojure”…



John Newman

unread,
Aug 20, 2016, 11:16:48 PM8/20/16
to Clojure Mailing List

I'd prefer getting rid of the symbol option. Some kind of deprecation warning for a version or two might be an idea though.


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

Christopher Small

unread,
Aug 21, 2016, 1:19:08 AM8/21/16
to Clojure
I couldn't help myself...



Alex Miller

unread,
Aug 21, 2016, 4:08:58 AM8/21/16
to Clojure
The documentation now includes the spec, which would explicilly mention the symbol, so this would not be tacitly hidden as you suggest. David is already working on porting these specs to ClojureScript so that issue is one we will imminently face.

So again I will state: while the current spec does not support the symbol version we are in an investigatory period and things may change before release.

Brian Marick

unread,
Aug 21, 2016, 6:25:03 PM8/21/16
to clo...@googlegroups.com
As an update. I’ve fixed the `ns` oopsie in Suchwow (one file), and the coincident `ns` oopsie in Midje (one file). But this happens when running Midje’s self-tests against Clojure 1.9alpha11:

> Exception in thread "main" java.lang.IllegalArgumentException: Call to clojure.core/fn did not conform to spec:
> In: [0] val: clojure.core.unify/var-unify fails spec: :clojure.core.specs/arg-list at: [:args :bs :arity-1 :args] predicate: vector?
> In: [0] val: clojure.core.unify/var-unify fails spec: :clojure.core.specs/args+body at: [:args :bs :arity-n] predicate: (cat :args :clojure.core.specs/arg-list :prepost (? map?) :body (* any?))
> :clojure.spec/args (clojure.core.unify/var-unify [varp v expr binds] (clojure.core/if-let [vb__10124__auto__ (binds v)] (clojure.core.unify/garner-unifiers varp vb__10124__auto__ expr binds) (clojure.core/if-let [vexpr__10125__auto__ (clojure.core/and (varp expr) (binds expr))] (clojure.core.unify/garner-unifiers varp v vexpr__10125__auto__ binds) (if (clojure.core.unify/occurs? varp v expr binds) (throw (java.lang.IllegalStateException. (clojure.core/str "Cycle found in the path " expr))) (clojure.core.unify/bind-phase binds v expr)))))
> , compiling:(clojure/core/unify.clj:82:18)

I suspect the problem is that Midje uses an old version of clojure.core.unify (0.5.2). I use that old version because a later versions (like the current 0.5.7) failed in my use case for no reason I could quickly understand. So I decided to just stick with the older, working version because forced upgrades make kittens suffer. And I am a kitten. And also: by semver, why should a satisfied user of 0.5.2 care about 0.5.7?

(Note: I don’t know whether clojure.core.unify 0.5.7 is conformant to whatever spec is breaking.)

So upgrading Midje to Clojure 1.0alpha11 is not turning out as simple as might be hoped.

I know that, pace Casablanca[*], the problems of a guy trying to provide an alternative to clojure.test don’t amount to a hill of beans in this crazy world. But I offer this datapoint for clojure.core.team consideration.

————

[*]

Rick: But I've got a job to do, too. Where I'm going, you can't follow. What I've got to do, you can't be any part of. Ilsa, I'm no good at being noble, but it doesn't take much to see that the problems of three little people don't amount to a hill of beans in this crazy world. Someday you'll understand that.
[Ilsa lowers her head and begins to cry]
Rick: Now, now...
[Rick gently places his hand under her chin and raises it so their eyes meet]
Rick: Here's looking at you kid.

lvh

unread,
Aug 21, 2016, 6:28:57 PM8/21/16
to clo...@googlegroups.com
FYI, while I disagree with your conclusion (I think we should go fix libraries instead), I ran into the same issue just now for roughly the same reason, except the thing that pulled in an old version of core.unify was core.typed, which pulls in 0.5.3 through core.contracts.
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

Alex Miller

unread,
Aug 21, 2016, 9:23:03 PM8/21/16
to Clojure


On Sunday, August 21, 2016 at 5:25:03 PM UTC-5, Brian Marick wrote:
As an update. I’ve fixed the `ns` oopsie in Suchwow (one file), and the coincident `ns` oopsie in Midje (one file). But this happens when running Midje’s self-tests against Clojure 1.9alpha11:

> Exception in thread "main" java.lang.IllegalArgumentException: Call to clojure.core/fn did not conform to spec:
> In: [0] val: clojure.core.unify/var-unify fails spec: :clojure.core.specs/arg-list at: [:args :bs :arity-1 :args] predicate: vector?
> In: [0] val: clojure.core.unify/var-unify fails spec: :clojure.core.specs/args+body at: [:args :bs :arity-n] predicate: (cat :args :clojure.core.specs/arg-list :prepost (? map?) :body (* any?))
> :clojure.spec/args  (clojure.core.unify/var-unify [varp v expr binds] (clojure.core/if-let [vb__10124__auto__ (binds v)] (clojure.core.unify/garner-unifiers varp vb__10124__auto__ expr binds) (clojure.core/if-let [vexpr__10125__auto__ (clojure.core/and (varp expr) (binds expr))] (clojure.core.unify/garner-unifiers varp v vexpr__10125__auto__ binds) (if (clojure.core.unify/occurs? varp v expr binds) (throw (java.lang.IllegalStateException. (clojure.core/str "Cycle found in the path " expr))) (clojure.core.unify/bind-phase binds v expr)))))
> , compiling:(clojure/core/unify.clj:82:18)

I suspect the problem is that Midje uses an old version of clojure.core.unify (0.5.2). I use that old version because a later versions (like the current 0.5.7) failed in my use case for no reason I could quickly understand.

Hard to help without more info.
 
So I decided to just stick with the older, working version because forced upgrades make kittens suffer. And I am a kitten. And also: by semver

I don't know that core.unify follows semver. I don't know that it doesn't, but that seems like an assumption.
 
, why should a satisfied user of 0.5.2 care about 0.5.7?

 
(Note: I don’t know whether clojure.core.unify 0.5.7 is conformant to whatever spec is breaking.)

It is. I released core.unify 0.5.7 about a month ago to fix the spec issue mentioned above.

Alex Miller

unread,
Aug 21, 2016, 9:24:20 PM8/21/16
to Clojure
On Sunday, August 21, 2016 at 5:28:57 PM UTC-5, lvh ‌ wrote:
FYI, while I disagree with your conclusion (I think we should go fix libraries instead), I ran into the same issue just now for roughly the same reason, except the thing that pulled in an old version of core.unify was core.typed, which pulls in 0.5.3 through core.contracts.

BTW, core.contracts was also updated to 0.0.6 last week to use the latest core.unify.

Luc

unread,
Aug 22, 2016, 5:41:44 AM8/22/16
to Clojure
That emacs joke gets my week started with some abdominal pain 😂😂
I support strictness 😬
Luc P.

Alex Miller

unread,
Aug 22, 2016, 11:08:57 AM8/22/16
to Clojure
I've added library related fixes related to core specs to an info page at:

Leon Grapenthin

unread,
Aug 22, 2016, 12:23:33 PM8/22/16
to Clojure
I welcome the strict checking over backwards compatibility for broken syntax. E. g. allowing things like symbols in the ns decl would require supporting that as a feature in future updates, analyzer code, other hosts etc. The Clojure devs should not have to worry things with so little use.

Still the error messages are simply far from good enough and that is what appears to me as the main problem OP has. If the compiler had pointed him to two fixes he could have done in a minute then he had not complained and instead felt more happy with his new compiler. 

Here is my story when I loaded a large codebase today with 1.9-alpha11:

Call to clojure.core/defn did not conform to spec: In: [1 0] val:
   
({:keys [money/major money/minor money/currency], :or #:money{major
   
0, minor 0, currency :EUR}}) fails spec:

   
:clojure.core.specs/arg-list at: [:args :bs :arity-1 :args]

   predicate
: (cat :args (* :clojure.core.specs/binding-form) :varargs
   
(? (cat :amp #{(quote &)} :form
   
:clojure.core.specs/binding-form))), Extra input In: [1 0] val:
   
{:keys [money/major money/minor money/currency], :or #:money{major
   
0, minor 0, currency :EUR}} fails spec:
   
:clojure.core.specs/arg-list at: [:args :bs :arity-n :bodies :args]
   predicate
: vector?  :clojure.spec/args (format-money [{:keys
   
[money/major money/minor money/currency], :or #:money{major 0,
   minor
0, currency :EUR}}] (str major "," (if (zero? minor) "-"
   minor
) " €"))


I know where the problem is immediately  because I looked at above error and quickly jumped to the code that didn't work. Then I guessed it right because I know what has been changed from Alex Release notes and because I had recently inquired on this board about :or destructoring and probably because I am a long time Clojure user. The problem is that :or in map destructuring with namespaced keywords was not officially supported before 1.9 (but sadly worked exactly opposite to how it is supported now)

Compare that the more common story of someone who has not followed every newspiece lately and just wants to upgrade from 1.8 to 1.9 - How could he tell whats wrong from above error message? Following above error message by looking up specs and following index paths like [1 0] is a manual process that costs and feels like debugging a difficultly hidden bug. The time/utility distance to a hand written macro assert like "Keys in :or destructoring must be unqualified symbols" currently does not justify the use of specs for such things. It's by far worse than the NPE Stacktraces popping up from nowhere that one learns to value and utilize after a month or so in Clojure. 

It seems that improving the error messages we can calculate from specs data is something that more people should think about and improve for 1.9. I'd be willing to invest time if needed / input is welcome. Alternatively a way to integrate custom error messages into specs directly could also be helpful.
 
(But I still don't really see how above spec tells me that I shouldn't use qualified symbols in :or destructoring - do you?)

Brian Marick

unread,
Aug 22, 2016, 6:11:27 PM8/22/16
to clo...@googlegroups.com

On Aug 22, 2016, at 11:23 AM, Leon Grapenthin <grapent...@gmail.com> wrote:

Still the error messages are simply far from good enough and that is what appears to me as the main problem OP has. 

This is important. Will the new, stricter error messages be improved before 1.9 is finalized? 


Oliver George

unread,
Aug 22, 2016, 7:45:16 PM8/22/16
to Clojure

I'm interested to see any discussion regarding this point.  No doubt translating spec data into more friendly formats has been discussed.

Getting the data right is clojure's problem.  That's the concrete foundation and building blocks required for tooling.  Seems like Rich has done spectacularly there.

Potentially it's up to tooling to do more with that data.  I'd love to hear Bruce (figwheel), Collin's (cursive) and Bozhidar (cider) opinions about that.  

Colin Fleming

unread,
Aug 22, 2016, 8:43:53 PM8/22/16
to clo...@googlegroups.com
I agree that the ability to get a machine-readable parse failure is very important for tooling. However I feel very strongly that the error messages that are printed by default on macro validation failures should be easily understandable, and the current ones are not. If we completely punt to tooling for this, firstly users will receive different (and different quality) error messages depending on the tool they're using, and anyone using plain vanilla Clojure will not get good errors at all.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.

Alex Miller

unread,
Aug 22, 2016, 8:50:19 PM8/22/16