Skip to content

Instantly share code, notes, and snippets.

@viebel
Last active January 21, 2020 11:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save viebel/ab64ed95820af42b366889a872dc28ac to your computer and use it in GitHub Desktop.
Save viebel/ab64ed95820af42b366889a872dc28ac to your computer and use it in GitHub Desktop.
The `spec` for `defn` arguments is called `:defn-args` and it is defined in [clojure.core.specs namespace](https://github.com/clojure/clojure/blob/clojure-1.9.0-alpha13/src/clj/clojure/core/specs.clj#L78-L84). But there are two problems with this implementation: 1. It has [not yet been ported](http://dev.clojure.org/jira/browse/CLJS-1813) to `cl…
(s/def ::local-name (s/and simple-symbol? #(not= '& %)))
(s/def ::binding-form
(s/or :sym ::local-name
:seq ::seq-binding-form
:map ::map-binding-form))
;; sequential destructuring
(s/def ::seq-binding-form
(s/and vector?
(s/conformer identity vec)
(s/cat :elems (s/* ::binding-form)
:rest (s/? (s/cat :amp #{'&} :form ::binding-form))
:as (s/? (s/cat :as #{:as} :sym ::local-name)))))
;; map destructuring
(s/def ::keys (s/coll-of ident? :kind vector?))
(s/def ::syms (s/coll-of symbol? :kind vector?))
(s/def ::strs (s/coll-of simple-symbol? :kind vector?))
(s/def ::or (s/map-of simple-symbol? any?))
(s/def ::as ::local-name)
(s/def ::map-special-binding
(s/keys :opt-un [::as ::or ::keys ::syms ::strs]))
(s/def ::map-binding (s/tuple ::binding-form any?))
(s/def ::ns-keys
(s/tuple
(s/and qualified-keyword? #(-> % name #{"keys" "syms"}))
(s/coll-of simple-symbol? :kind vector?)))
(s/def ::map-bindings
(s/every (s/or :mb ::map-binding
:nsk ::ns-keys
:msb (s/tuple #{:as :or :keys :syms :strs} any?)) :into {}))
(s/def ::map-binding-form (s/merge ::map-bindings ::map-special-binding))
;; bindings
(s/def ::binding (s/cat :binding ::binding-form :init-expr any?))
(s/def ::bindings (s/and vector? (s/* ::binding)))
;; defn, defn-, fn
(defn arg-list-unformer [a]
(vec
(if (and (coll? (last a)) (= '& (first (last a))))
(concat (drop-last a) (last a))
a)))
(s/def ::arg-list
(s/and
vector?
(s/conformer identity arg-list-unformer)
(s/cat :args (s/* ::binding-form)
:varargs (s/? (s/cat :amp #{'&} :form ::binding-form)))))
(s/def ::args+body
(s/cat :args ::arg-list
:prepost (s/? map?)
:body (s/* any?)))
(s/def ::defn-args
(s/cat :name simple-symbol?
:docstring (s/? string?)
:meta (s/? map?)
:bs (s/alt :arity-1 ::args+body
:arity-n (s/cat :bodies (s/+ (s/spec ::args+body))
:attr (s/? map?)))))
@bhb
Copy link

bhb commented Jan 16, 2017

Thanks for this! But why are ::arg-list, ::args+body, and ::defn-args defined twice?

@viebel
Copy link
Author

viebel commented Feb 5, 2017

Thx @bhb! I have removed the redundancy. Fixed.
Have you read this article? http://blog.klipse.tech/clojure/2016/10/10/defn-args.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment