store/[org.clojure/clojure "1.8.0"] clj::clojure.core/defn

Community Documentation

Edit

Arities

[name docstring? attr-map? [params*] prepost-map? body]
[name docstring? attr-map? ([params*] prepost-map? body)+]

Docs

Same as (def name (fn [params* ] exprs*)) or (def name (fn ([params* ] exprs*)+)) with any docstring or attrs added to the Var metadata. Function parameter bindings support destructuring.

Pre/Post conditions

prepost-map is a map with the optional keys :pre and :post. Other keys are ignored. Each should be a vector of forms respectively to be run as preconditions or postconditions. Each form is treated as an assertion statement, and if any form evaluates false (see clj::clojure.core/boolean) then an AssertionError is thrown. Preconditions may refer to argument bindings. Postconditions may refer to argument bindings, and to the return value of the fn as %.

Notes

  • It is poor style to parenthesize a single arity. Eg, (defn foo [x] x) rather than (defn foo ([x] x)).
  • It is poor style to use reader metadata on the name rather than to use the attr map.
  • It is poor style to put a :doc entry in the attr map rather than use the docstring.

Source

(def 

 ^{:doc "Same as (def name (fn [params* ] exprs*)) or (def
    name (fn ([params* ] exprs*)+)) with any doc-string or attrs added
    to the var metadata. prepost-map defines a map with optional keys
    :pre and :post that contain collections of pre or post conditions."
   :arglists '([name doc-string? attr-map? [params*] prepost-map? body]
                [name doc-string? attr-map? ([params*] prepost-map? body)+ attr-map?])
   :added "1.0"}
 defn (fn defn [&form &env name & fdecl]
        ;; Note: Cannot delegate this check to def because of the call to (with-meta name ..)
        (if (instance? clojure.lang.Symbol name)
          nil
          (throw (IllegalArgumentException. "First argument to defn must be a symbol")))
        (let [m (if (string? (first fdecl))
                  {:doc (first fdecl)}
                  {})
              fdecl (if (string? (first fdecl))
                      (next fdecl)
                      fdecl)
              m (if (map? (first fdecl))
                  (conj m (first fdecl))
                  m)
              fdecl (if (map? (first fdecl))
                      (next fdecl)
                      fdecl)
              fdecl (if (vector? (first fdecl))
                      (list fdecl)
                      fdecl)
              m (if (map? (last fdecl))
                  (conj m (last fdecl))
                  m)
              fdecl (if (map? (last fdecl))
                      (butlast fdecl)
                      fdecl)
              m (conj {:arglists (list 'quote (sigs fdecl))} m)
              m (let [inline (:inline m)
                      ifn (first inline)
                      iname (second inline)]
                  ;; same as: (if (and (= 'fn ifn) (not (symbol? iname))) ...)
                  (if (if (clojure.lang.Util/equiv 'fn ifn)
                        (if (instance? clojure.lang.Symbol iname) false true))
                    ;; inserts the same fn name to the inline fn if it does not have one
                    (assoc m :inline (cons ifn (cons (clojure.lang.Symbol/intern (.concat (.getName ^clojure.lang.Symbol name) "__inliner"))
                                                     (next inline))))
                    m))
              m (conj (if (meta name) (meta name) {}) m)]
          (list 'def (with-meta name m)
                ;;todo - restore propagation of fn name
                ;;must figure out how to convey primitive hints to self calls first
								;;(cons `fn fdecl)
								(with-meta (cons `fn fdecl) {:rettag (:tag m)})))))

Example 1

Edit
(defn bar
  ([a b]   (bar a b 100))
  ([a b c] (* a b c)))
;; => #'user/bar

(bar 5 6)
;; => 3000

(bar 5 6 2)
;; => 60

Example 2

Edit
;; You can use destructuring to have keyword arguments. This would be a
;; pretty verbose version of map (in an example a bit more verbose than
;; the first above):

(defn keyworded-map
  [& {function :function sequence :sequence}]
  (map function sequence))

;; You can call it like this:

(keyworded-map :sequence [1 2 3] :function #(+ % 2))
;; => (3 4 5)

;; The declaration can be shortened with ":keys" if your local variables 
;; should be named in the same way as your keys in the map:

(defn keyworded-map
  [& {:keys [function sequence]}]
  (map function sequence))

Example 3

Edit
(defn foo [a b c]
  (* a b c))
;; => #'user/foo

(foo 1 2 3)
;; => 6

(defn bar [a b & [c]]
  (if c
    (* a b c)
    (* a b 100)))
;; => #'user/bar

(bar 5 6)
;; => 3000

(bar 5 6 2)
;; => 60

(defn baz [a b & {:keys [c d] :or {c 10 d 20}}]
  (* a b c d))
;; => #'user/baz

(baz 2 3)
;; => 1200

(baz 2 3 :c 5)
;; => 600

(baz 2 3 :c 5 :d 6)
;; => 180

(defn boo [a b & {:keys [c d] :or {c 10 d 20} :as all-specified}]
  (println all-specified)
  (* a b c d))
;; => #'user/boo

(boo 2 3)
;; > nil
;; => 1200

(boo 2 3 :c 5)
;; > {:c 5}
;; => 600

(boo 1 2 :d 3 :c 4)
;; > {:c 4, :d 3}
;; => 24

Example 4

Edit
;; Show off argument destructuring in the defn macro

(defn somefn [req1 req2 ;; required params
              & {:keys [a b c d e] ;; optional params
                 :or {a 1 ;; optional params with preset default values other than the nil default
                      ;; b takes nil if not specified on call
                      c 3 ;; c is 3 when not specified on call
                      d 0 ;; d is 0 --//--
                      ;; e takes nil if not specified on call
}
                 :as mapOfParamsSpecifiedOnCall ;; takes nil if no extra params(other than the required ones) are specified on call
}]
  (println req1 req2 mapOfParamsSpecifiedOnCall a b c d e))

(somefn 9 10 :b 2 :d 4)
;; > 9 10 {:b 2, :d 4} 1 2 3 4 nil
;; => nil

(somefn)
;; => ArityException Wrong number of args (0) passed to: funxions$somefn  ;clojure.lang.AFn.throwArity (AFn.java:437)

(somefn 9 10)
;; > 9 10 nil 1 nil 3 0 nil
;; => nil

(somefn 9 10 :x 123)
;; > 9 10 {:x 123} 1 nil 3 0 nil
;; => nil

(somefn 9 10 123)
;; => IllegalArgumentException No value supplied for key: 123  ;clojure.lang.PersistentHashMap.create (PersistentHashMap.java:77)

(somefn 9 10 123 45)
;; > 9 10 {123 45} 1 nil 3 0 nil
;; => nil

(try (somefn 9 10 123)
     (catch IllegalArgumentException e
       (println "caught:" e)))
;; > caught: #<IllegalArgumentException java.lang.IllegalArgumentException: No value supplied for key: 123>
;; => nil

Uses on crossclj