How get function name in body?

已查看 509 次
跳至第一个未读帖子

damon kwok

未读,
2021年4月28日 09:35:112021/4/28
收件人 Clojure

How get function name in body?

(defn add [a b]
 ;; how get "add" here?
  (+ a b))

Blake Watson

未读,
2021年4月28日 16:05:332021/4/28
收件人 clo...@googlegroups.com
Well, it seems like it should be possible. At the end of the defn macro, metadata is attached to a function, so you can see the name with:

```
(meta #'my.ns/add)
```

And you could do this inside the function:

```
(defn add [a b]
  (let [name (:name (meta #'add))]
    (str a b name)))
```

But that can't be what you want. The thing is, the actual function body doesn't know that it's being bound to the symbol "add" and making it aware would seem to contravene a number of league bylaws.

That is to say, the macro "defn" attaches the meta-information to the symbol #add, not to the code that follows.

You could certainly make your own macro to make the bound variable name visible to the body of the code, but I'm not sure you can do that with the built-in defn.

Sean Corfield

未读,
2021年4月28日 19:26:542021/4/28
收件人 Clojure Mailing List
Consider that:

(defn add [a b] (+ a b))

is expanded to (something like):

(def add (fn [a b] (+ a b)))

So the actual code that runs is an anonymous function, which is bound to the (global) var #'add -- the function itself has no name.

dev=> (macroexpand '(defn add [a b] (+ a b)))

(def add (clojure.core/fn ([a b] (+ a b))))

dev=> (defn add [a b] (+ a b))

#'dev/add

dev=> add

#object[dev$add 0x72f91c91 "dev$add@72f91c91"]


So the value of add is an object, with type dev$add, a class that implements a bunch of things including clojure.lang.IFn and java.lang.Runnable and java.util.concurrent.Callable etc.

The "name" of function is hard to get to, inside a function because of that.

One way to figure that out at runtime is to dig into the stack, which you can get at by creating an exception and inspecting it: be aware that this is an expensive operation!

dev=> (defn add [a b] 

 #_=>   (let [frame (first (:trace (Throwable->map (ex-info "" {}))))] 

 #_=>     (println (demunge (name (first frame)))) 

 #_=>     (+ a b)))

#'dev/add

dev=> (add 1 2)

dev/add

3

dev=> 


demunge is what turns Clojure's generated classnames back into readable source names.

The more important question is: why do you want the function's name inside the body?
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- https://corfield.org/
World Singles Networks, LLC. -- https://worldsinglesnetworks.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/clojure/5c533ccf-ec0b-4c84-b759-851f1fe3b3can%40googlegroups.com.

Remi Forax

未读,
2021年4月29日 15:08:032021/4/29
收件人 clojure
De: "Sean Corfield" <se...@corfield.org>
À: "clojure" <clo...@googlegroups.com>
Envoyé: Jeudi 29 Avril 2021 01:26:34
Objet: Re: How get function name in body?
Consider that:

(defn add [a b] (+ a b))

is expanded to (something like):

(def add (fn [a b] (+ a b)))

So the actual code that runs is an anonymous function, which is bound to the (global) var #'add -- the function itself has no name.

dev=> (macroexpand '(defnadd [a b] (+ a b)))

(def add (clojure.core/fn ([a b] (+ a b))))

dev=> (defnadd [a b] (+ a b))

#'dev/add

dev=> add

#object[dev$add 0x72f91c91 "dev$add@72f91c91"]


So the value of add is an object, with type dev$add, a class that implements a bunch of things including clojure.lang.IFn and java.lang.Runnable and java.util.concurrent.Callable etc.

The "name" of function is hard to get to, inside a function because of that.

One way to figure that out at runtime is to dig into the stack, which you can get at by creating an exception and inspecting it: be aware that this is an expensive operation!

dev=> (defnadd [a b]

#_=> (let [frame (first (:trace (Throwable->map (ex-info"" {}))))]

#_=> (println (demunge (name (first frame))))

#_=> (+ a b)))

#'dev/add

dev=> (add 1 2)

dev/add

3

dev=>


demunge is what turns Clojure's generated classnames back into readable source names.

Instead of using the stacktrace of java.lang.Throwable, you can use StackWalker.getCallerClass() if you are using a jdk 9+


The more important question is: why do you want the function's name inside the body?
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- https://corfield.org/
World Singles Networks, LLC. -- https://worldsinglesnetworks.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Sean Corfield

未读,
2021年4月29日 16:35:452021/4/29
收件人 Clojure Mailing List
That's definitely nicer:

dev=> (defn add [a b]

 #_=>   (println (demunge (.getName (.getCallerClass (java.lang.StackWalker/getInstance java.lang.StackWalker$Option/RETAIN_CLASS_REFERENCE)))))

 #_=>   (+ a b))

#'dev/add

dev=> (add 1 2)

dev/add

3

dev=> 


Thanks, Rémi!

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

damon kwok

未读,
2021年4月30日 01:27:372021/4/30
收件人 Clojure

Clojure 1.10.1
       (defn haha []
           (println (demunge (.getName (.getCallerClass (java.lang.StackWalker/getInstance java.lang.StackWalker$Option/RETAIN_CLASS_REFERENCE))))))
Syntax error compiling at (REPL:1:23).
Unable to resolve symbol: demunge in this context

Sean Corfield

未读,
2021年4月30日 01:41:232021/4/30
收件人 Clojure Mailing List
Sorry, it's clojure.repl/demunge so it depends on how you run your REPL and evaluate code -- in my case, clojure.repl symbols were all referred in. In your case, you'll need to require clojure.repl and refer it in:

(require '[clojure.repl :refer [demunge])

or make that part of your ns form as:

  (:require [clojure.repl :refer [demunge]])

--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- https://corfield.org/
World Singles Networks, LLC. -- https://worldsinglesnetworks.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Justin Smith

未读,
2021年5月3日 11:21:182021/5/3
收件人 Clojure
there's a handy trick for pulling in the standard repl aliases / refers:

(cmd)user=> clojure.main/repl-requires
[[clojure.repl :refer (source apropos dir pst doc find-doc)] [clojure.java.javadoc :refer (javadoc)] [clojure.pprint :refer (pp pprint)]]
(ins)user=> (ns foo.bar)
nil
(ins)foo.bar=> (doc fn)
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: doc in this context
(ins)foo.bar=> (apply require clojure.main/repl-requires)
nil
(cmd)foo.bar=> (doc fn)
-------------------------
clojure.core/fn
  (fn name? [params*] exprs*)
  (fn name? ([params*] exprs*) +)
([& sigs])
Special Form
...

Brent Millare

未读,
2021年5月4日 10:01:392021/5/4
收件人 Clojure
That's a useful tip! Thanks
回复全部
回复作者
转发
0 个新帖子