Share your thoughts in the 2024 State of Clojure Survey!

Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by

It seems like it should be possible to use a macro for generating a attribute map for defn but it appears as if the defn spec requires a literal map. See example below, which makes the spec fail.

   (defmacro example [f output input]
     `{:test (fn [] (is (= (~f ~input) ~output)))})

   (defn tails
     (example tails
              [1 2 3 4]
              '([1 2 3 4] (2 3 4) (3 4) (4)))
     [coll]
     (take-while some? (iterate next coll)))

->

 Call to clojure.core/defn did not conform to spec.
       #:clojure.spec.alpha{:problems
                            ({:path [:fn-tail :arity-1 :params],
                              :pred clojure.core/vector?,
                              :val
                              (example
                               tails
                               [1 2 3 4]
                               '([1 2 3 4] (2 3 4) (3 4) (4))),
                              :via
                              [:clojure.core.specs.alpha/defn-args
                               :clojure.core.specs.alpha/params+body
                               :clojure.core.specs.alpha/param-list
                               :clojure.core.specs.alpha/param-list],
                              :in [1]}
                             {:path [:fn-tail :arity-n :bodies :params],
                              :pred clojure.core/vector?,
                              :val example,
                              :via
                              [:clojure.core.specs.alpha/defn-args
                               :clojure.core.specs.alpha/params+body
                               :clojure.core.specs.alpha/params+body
                               :clojure.core.specs.alpha/params+body
                               :clojure.core.specs.alpha/param-list
                               :clojure.core.specs.alpha/param-list],
                              :in [1 0]}),
                            :spec
                            #object[clojure.spec.alpha$regex_spec_impl$reify__2510 0x643aeb02 "clojure.spec.alpha$regex_spec_impl$reify__2510@643aeb02"],
                            :value
                            (tails
                             (example tails [1 2 3 4] '([1 2 3 4] (2 3 4) (3 4) (4)))
                             [coll]
                             (take-while some? (iterate next coll))),
                            :args
                            (tails
                             (example tails [1 2 3 4] '([1 2 3 4] (2 3 4) (3 4) (4)))
                             [coll]
                             (take-while some? (iterate next coll)))}
                     alpha.clj:  712  clojure.spec.alpha/macroexpand-check
                     alpha.clj:  704  clojure.spec.alpha/macroexpand-check
                      AFn.java:  156  clojure.lang.AFn/applyToHelper
                      AFn.java:  144  clojure.lang.AFn/applyTo
                      Var.java:  705  clojure.lang.Var/applyTo
                 Compiler.java: 6974  clojure.lang.Compiler/checkSpecs
                 Compiler.java: 6992  clojure.lang.Compiler/macroexpand1
                 Compiler.java: 7079  clojure.lang.Compiler/macroexpand
                 Compiler.java: 7165  clojure.lang.Compiler/eval
                 Compiler.java: 7136  clojure.lang.Compiler/eval
                      core.clj: 3202  clojure.core/eval
                      core.clj: 3198  clojure.core/eval
        interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                      AFn.java:  152  clojure.lang.AFn/applyToHelper
                      AFn.java:  144  clojure.lang.AFn/applyTo
                      core.clj:  667  clojure.core/apply
                      core.clj: 1977  clojure.core/with-bindings*
                      core.clj: 1977  clojure.core/with-bindings*
                   RestFn.java:  425  clojure.lang.RestFn/invoke
        interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                      main.clj:  437  clojure.main/repl/read-eval-print/fn
                      main.clj:  437  clojure.main/repl/read-eval-print
                      main.clj:  458  clojure.main/repl/fn
                      main.clj:  458  clojure.main/repl
                      main.clj:  368  clojure.main/repl
                   RestFn.java:  137  clojure.lang.RestFn/applyTo
                      core.clj:  667  clojure.core/apply
                      core.clj:  662  clojure.core/apply
                    regrow.clj:   20  refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
                   RestFn.java: 1523  clojure.lang.RestFn/invoke
        interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
        interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
        interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                      AFn.java:   22  clojure.lang.AFn/run
                   session.clj:  202  nrepl.middleware.session/session-exec/main-loop/fn
                   session.clj:  201  nrepl.middleware.session/session-exec/main-loop
                      AFn.java:   22  clojure.lang.AFn/run
                   Thread.java:  829  java.lang.Thread/run

1 Answer

+1 vote
by
selected by
 
Best answer

No, the spec is not too restrictive. The defn macro does not support this. You can turn off spec macro checking with -Dclojure.spec.skip-macros=true.

You can write a macro that wraps defn and emits the defn with the eval'ed thing if you like.

...