Clojure: How to find out the arity of function at runtime?

前端 未结 7 1404
野趣味
野趣味 2021-01-31 02:08

Given a function object or name, how can I determine its arity? Something like (arity func-name) .

I hope there is a way, since arity is pretty central in C

7条回答
  •  醉梦人生
    2021-01-31 02:23

    My heart bled (covered all the cases).

    (defn arity
      "Returns the maximum arity of:
        - anonymous functions like `#()` and `(fn [])`.
        - defined functions like `map` or `+`.
        - macros, by passing a var like `#'->`.
    
      Returns `:variadic` if the function/macro is variadic."
      [f]
      (let [func (if (var? f) @f f)
            methods (->> func class .getDeclaredMethods
                         (map #(vector (.getName %)
                                       (count (.getParameterTypes %)))))
            var-args? (some #(-> % first #{"getRequiredArity"})
                            methods)]
        (if var-args?
          :variadic
          (let [max-arity (->> methods
                               (filter (comp #{"invoke"} first))
                               (sort-by second)
                               last
                               second)]
            (if (and (var? f) (-> f meta :macro))
              (- max-arity 2) ;; substract implicit &form and &env arguments
              max-arity)))))
    
    (use 'clojure.test)
    
    (defmacro m ([a]) ([a b]))
    (defmacro mx [])
    
    (deftest test-arity
      (testing "with an anonymous #(… %1) function"
        (is (= 1           (arity #(+ % 32))))
        (is (= 1           (arity #(+ %1 32))))
        (is (= 2           (arity #(+ %1 %2))))
        (is (= 13          (arity #(+ %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 %13))))
        (is (= :variadic   (arity #(apply + %&))))
        (is (= :variadic   (arity #(apply + % %&)))))
      (testing "with an anonymous (fn [] …) function"
        (testing "single body"
          (is (= 0         (arity (fn []))))
          (is (= 1         (arity (fn [a]))))
          (is (= 2         (arity (fn [a b]))))
          (is (= 20        (arity (fn [a b c d e f g h i j k l m n o p q r s t]))))
          (is (= :variadic (arity (fn [a b & more])))))
        (testing "multiple bodies"
          (is (= 0         (arity (fn ([])))))
          (is (= 1         (arity (fn ([a])))))
          (is (= 2         (arity (fn ([a]) ([a b])))))
          (is (= :variadic (arity (fn ([a]) ([a b & c])))))))
      (testing "with a defined function"
        (is (= :variadic   (arity map)))
        (is (= :variadic   (arity +)))
        (is (= 1           (arity inc))))
      (testing "with a var to a macro"
        (is (= :variadic   (arity #'->)))
        (is (= 2           (arity #'m)))
        (is (= 0           (arity #'mx)))))
    
    (run-tests)
    

提交回复
热议问题