How to get function's name as string in Clojure?

后端 未结 3 1508
佛祖请我去吃肉
佛祖请我去吃肉 2021-02-13 03:50

How can you get function\'s name as string in Clojure?

What I have so far doesn\'t look anywhere near idiomatic:

(defn fn-name
  [f]
  (first (re-find #\         


        
相关标签:
3条回答
  • 2021-02-13 04:37

    EDIT: I found a better way Clojure includes a function called demunge. So instead of re-inventing the wheel, just use it ;)

    (clojure.repl/demunge (str map?));; "clojure.core/map?@2b68895c"
    

    Or if you want a prettified version

    (defn- pretty-demunge
      [fn-object]
      (let [dem-fn (demunge (str fn-object))
            pretty (second (re-find #"(.*?\/.*?)[\-\-|@].*" dem-fn))]
        (if pretty pretty dem-fn)))
    
    (pretty-demunge map?);; "clojure.core/map?"
    

    I think there is a more clean way to do this. I ran into the same problem of wanting to know the name of function gotten as an argument in a function. I couldn't use the macro because I needed to map it so here is what I've got: (assume string? is the function passed as argument)

    (clojure.string/replace (second (re-find #"^.+\$(.+)\@.+$" (str string?)))
                            #"\_QMARK\_" "?")
    ; string?
    

    Of course this is not a complete solution but I'm sure you can work your way through from here. Basically Clojure mangles the function name into something it can use. So the basic though is obviously: you need to unmangle that ! Which is pretty easy since str works on any function :D, returning the mangled name of the function.

    By the way, this also works

    (def foo string?)
    (clojure.string/replace (second (re-find #"^.+\$(.+)\@.+$" (str foo)))
                            #"\_QMARK\_" "?")
    ; string?
    

    Have fun

    0 讨论(0)
  • 2021-02-13 04:39

    For functions defined with the defn form:

    user> (-> #'map meta :name)
    map
    user> (defn nothing [& _])
    #'user/nothing
    user> (-> #'nothing meta :name)
    nothing
    

    This requires access to the var, rather than just the function value the var holds.

    0 讨论(0)
  • 2021-02-13 04:52

    I suggested an improvement to @carocad's answer in a comment. Since I also needed to do this, and I needed to do it in both clj and cljs, here is what I came up with:

    (ns my-ns.core
      (:require [clojure.string :as s]
                #?(:clj [clojure.main :refer [demunge]])))
    
    (defn fn-name
      [f]
      #?(:clj
          (as-> (str f) $
                (demunge $)
                (or (re-find #"(.+)--\d+@" $)
                    (re-find #"(.+)@" $))
                (last $))
         :cljs
          (as-> (.-name f) $
                (demunge $)
                (s/split $ #"/")
                ((juxt butlast last) $)
                (update $ 0 #(s/join "." %))
                (s/join "/" $))))
    

    Note that cljs.core has its own demunge built in and can't access clojure.main.


    Edit

    Note that when you do (with-meta a-fn {...}), it returns a clojure.lang.AFunction in clojure, which hides the underlying name and namespace information. There may be other situations like that, I'm not sure. With fn literal forms, you can do ^{...} (fn ...) instead of (with-meta (fn ...) {...}) and it won't return a clojure.lang.AFunction, but that workaround won't work with predefined functions. I haven't tested any of this in clojurescript to see if it works the same way.

    Note also that anonymous functions in clojure will always end in fn, as in "my-ns.core/fn". Normally these functions would have a "--[0-9]+" at the end, but the regex above removes that. You could modify the function above and make an exception for anonymous functions. As long as the lambda has an internal name, that will be used, e.g.:

    (fn-name (fn abc [x] x)) ;;=> "my-ns.core/abc"
    

    Again, I haven't tested any of these notes in clojurescript yet.

    0 讨论(0)
提交回复
热议问题