How do I use reflection in Clojure to extract the function name of a symbol that evaluates to a function?
(require '[clojure.reflect :as r])
(defn foo [x] x)
(r/reflect foo)
=> {#clojure.reflect.Field ... 'gobbledygook}
Something about (map :name (:members (r/reflect foo)))?
No, please do not use str because it behaves differently. In my example, here is what I've got:
user=> (defn foo [x] x)
#'user/foo
user=> (str foo)
"user$foo#7cb20059"
Instead, use combination of meta and var calls:
(-> foo var meta)
This code returns a map of variable's metadata. The key :name holds the function name as a string:
(-> foo var meta :name)
foo
If you also want to have an ns's name, do the following:
user=> (-> foo var meta :ns ns-name)
user
It gets the namespace object and returns its name with the ns-name function as a string.
Those methods do not depend on Clojure or REPL version and thus are more maintainable.
Looks like I can use str:
(str foo)
=> "#'my-project/foo"
Related
As a minimal example of what I want to do:
(defn mkfn [func]
(fn func [a] (print "I am a function")))
(mkfn 'x) ; => #function[user/mkfn/func--10871]
(type x)
(x)
The last two both result in:
Syntax error compiling at (conjure-log-12628.cljc:1:1).
Unable to resolve symbol: x in this context
I'm not sure why this doesn't work since fn takes symbols as input and 'x is a symbol. I'm also not sure how to accomplish this task.
For that matter:
user=> (def (eval 'y) 3)
Syntax error compiling def at (conjure-log-12628.cljc:1:1).
user=> (def 'y 3)
Syntax error compiling def at (conjure-log-12628.cljc:1:1).
First argument to def must be a Symbol
First argument to def must be a Symbol
user=> (type 'y)
clojure.lang.Symbol
Other things that don't work:
(defn mkfn [func]
(fn (sympol func) [a] (print "i am a function")))
(symbol "y") ; => y ; a symbol
(def (symbol "y") 3) ; => an err
You will probably need a macro. It seems that you want to call that function by the provided name, so you also have to replace fn with defn.
And you have to be careful about a number of arguments, because function x with argument vector [a] must be called with one argument, and not like (x).
(defmacro mkfn [func]
`(defn ~func [~'a]
(print "I am a function")))
(mkfn x)
=> #'user/x
(x 1)
I am a function=> nil
There is also other way, using intern, so you can completely avoid writing macros:
(intern *ns* 'x (fn [a] (print "I am a function")))
=> #object...
(x 1)
I am a function=> nil
Example with intern:
(defn mkfn [func]
(intern *ns* func (fn [a] (print "I am a function"))))
=> #'user/mkfn
(mkfn 'y)
=> #'user/y
(y 1)
I am a function=> nil
As for your errors, def is a special form, so it has different evaluation rules. It doesn't evaluate the first argument, which has to be a symbol- and (unevaluated) (eval 'y), 'y or (symbol "y") aren't symbols, while y is.
You gonna need a macro for that since you need code writing code.
(defmacro mkfn [func]
`(fn ~func [~'a] ...))
There 2 ways of doing it, either function plus eval or macro. If you really want to programatically create a new function with your chosen name, the macro solution is the way to go.
The function + eval solution is instructive, but you'll have to either quote the function name when you call it (via a 2nd eval) or save the created function in another variable when you create it.
If you are interested in writing macros, please see this other question first: How do I write a Clojure threading macro?
For the function + eval, we can start with my favorite template project and add the following:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn maker-eval
[fn-sym]
(let [ll (list 'fn 'anon-fn [] (str "I am " fn-sym))]
(spyx :eval ll)
(eval ll)))
(verify
(let [anon-fn-1 (maker-eval 'aaa)]
(is= "I am aaa" (anon-fn-1))) ; need the temp local variable
(let [anon-fn-2 (maker-eval 'bbb)]
(is= "I am bbb" (anon-fn-2))) ; need the temp local variable
)
and we can see the creation and use of the function, along with printed output:
:eval ll => (fn anon-fn [] "I am aaa")
:eval ll => (fn anon-fn [] "I am bbb")
For the macro version, we type
(defn maker-macro-impl
[fn-sym]
(let [ll `(defn ~fn-sym [] (str "I am " (str (quote ~fn-sym))))]
(spyx :macro ll)
ll))
(defmacro maker-macro
[fn-sym] (maker-macro-impl fn-sym))
(verify
(let [anon-fn-3 (maker-macro-impl 'ccc)]
(is= anon-fn-3 (quote
(clojure.core/defn ccc [] (clojure.core/str "I am " (clojure.core/str (quote ccc)))))))
(maker-macro ddd)
(is= (ddd) "I am ddd"))
and see printed:
:macro ll => (clojure.core/defn ccc [] (clojure.core/str "I am " (clojure.core/str (quote ccc))))
Note that the local variable anon-fn-3 was only used to test the maker-macro-impl function, but was not needed to call the newly-created function ddd
at the end of the unit test.
If there is a class and a json:
(defclass foo ()
((bar :initarg :bar)))
(defvar *input* "\{ \"bar\" : 3 }")
How to convert *input* into an instance of foo using cl-json library?
I guess it should be something like:
(with-decoder-simple-clos-semantics
(let ((*prototype-name* 'foo))
(decode-json-from-string *input*)))
But it produces:
Invalid SB-MOP:SLOT-DEFINITION initialization: the
initialization argument :NAME was constant: :BAR.
[Condition of type SB-PCL::SLOTD-INITIALIZATION-ERROR]
What am I doing wrong?
The cause of the error is that cl-json:*json-symbols-package* is bound to the KEYWORD package: when JSON keys are turned into symbols, they become keywords which apparently are not valid as slot names.
Fluid objects
The following works:
(let ((json:*json-symbols-package* (find-package :cl-user)))
(json:with-decoder-simple-clos-semantics
(json:decode-json-from-string "{ \"bar\" : 3 }")))
(note: you only need backslashes before double-quote characters)
You obtain a FLUID-OBJECT.
Prototype key in JSON data
Now, you can also define your own class:
(in-package :cl-user)
(defclass foo ()
((bar :initarg :bar)))
And then, the JSON needs to have a "prototype" key:
(let ((json:*json-symbols-package* (find-package :cl-user)))
(json:with-decoder-simple-clos-semantics
(json:decode-json-from-string
"{ \"bar\" : 3 ,
\"prototype\" : { \"lispClass\" : \"foo\",
\"lispPackage\" : \"cl-user\" }}")))
The above returns an instance of FOO.
You can use a different key than "prototype" by rebinding *prototype-name*.
Force a default prototype (hack)
Without changing the existing library code, you can hack around it to change the behavior of the decoding step. The code is organized around special variables that are used as callbacks at various point of the parsing, so it is a matter of wrapping the expected function with your own:
(defun wrap-for-class (class &optional (fn json::*end-of-object-handler*))
(let ((prototype (make-instance 'json::prototype :lisp-class class)))
(lambda ()
;; dynamically rebind *prototype* right around calling fn
(let ((json::*prototype* prototype))
(funcall fn)))))
The above creates a prototype object for the given class (symbol), capture the current binding of *end-of-object-handler*, and returns a closure that, when called, bind *prototype* to the closed-over prototype instance.
Then, you call it as follows:
(let ((json:*json-symbols-package* *package*))
(json:with-decoder-simple-clos-semantics
(let ((json::*end-of-object-handler* (wrap-for-class 'foo)))
(json:decode-json-from-string
"{ \"bar\" : 3 }"))))
And you have an instance of FOO.
Recursion
Note that if you define foo as follows:
(defclass foo ()
((bar :initarg :bar :accessor bar)
(foo :initarg :foo :accessor foo)))
Then the hack also reads nested JSON objects as FOO:
(let ((json:*json-symbols-package* *package*))
(json:with-decoder-simple-clos-semantics
(let ((json::*end-of-object-handler* (wrap-for-class 'foo)))
(json:decode-json-from-string
"{ \"bar\" : 3, \"foo\" : { \"bar\" : 10} }"))))
=> #<FOO {1007A70E23}>
> (describe *)
#<FOO {1007A70E23}>
[standard-object]
Slots with :INSTANCE allocation:
BAR = 3
FOO = #<FOO {1007A70D53}>
> (describe (foo **))
#<FOO {1007A70D53}>
[standard-object]
Slots with :INSTANCE allocation:
BAR = 10
FOO = #<unbound slot>
I'm trying to create a Clojure function, that returns another function with a custom name. My attempts so far:
(defn function-with-custom-name [name] (fn name [] 42))
(function-with-custom-name "hello")
# --> #object[lang.main$function_with_custom_name$name__4660 0xa6afefa "lang.main$function_with_custom_name$name__4660#a6afefa"]
# syntactically ok, but its name is 'name' and not 'hello'
(defn function-with-custom-name [name] (fn (symbol name) [] 42))
# --> CompilerException java.lang.IllegalArgumentException: Parameter declaration symbol should be a vector, compiling:(/tmp/form-init3365336074265515078.clj:1:40)
(defn function-with-custom-name [name] (fn '(symbol name) [] 42))
# --> CompilerException java.lang.IllegalArgumentException: Parameter declaration quote should be a vector, compiling:(/tmp/form-init3365336074265515078.clj:1:40)
I understand that fn is a macro, and therefore proper quoting is probably important for the parameter, but as per above, I could not get it right, but I'm 99% sure there is a way, since (looking at the source of fn), the only criteria is that the first parameter should be recognized as a symbol.
Any hints on how to do this?
EDIT: Use-case, as asked in the comment: I'm writing a simple language interpreter in Clojure, which (among other things) lets you create functions. The functions from my language are currently represented by anonymous Clojure functions. However, it would make debugging the interpreter much easier, if the Clojure functions also did have a name.
EDIT2: The first edit made me think, and I came to the conclusion that I cannot use macro-based solutions for this problem, since I need to create the functions run-time (and, as far as I remember, macros can only work at compile-time). --> Changed the question title for clarity. Still, please don't delete the macro-based answers, since they give helpful insight.
You can use defmacro.
(defmacro function-with-custom-name [name]
`(fn ~(symbol name) ([] 42)))
you can also do it in runtime, without using macros, using namespace functions instead. It can give you the way to register functions from some input for example (i can't really find any good reason for this though, but maybe it's just me)
user> (defn runtime-defn [f-name f]
(intern (ns-name *ns*) (symbol f-name) f))
#'user/runtime-defn
user> (runtime-defn "my-fun" #(* 100 %))
#'user/my-fun
user> (my-fun 123)
;;=> 12300
user> (runtime-defn (read) #(* 200 %))
#input "danger!!!"
#'user/danger!!!
user> (danger!!! 1)
;;=> 200
Update:
For the simple version, you can use defmacro. For a more complicated version (such as used by the potemkin library) you need to mess around with creating a var and "intern-ing" it into the clojure namespace. This is accomplished via clojure.core/intern:
(ns tst.demo.core
(:use demo.core tupelo.test )
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(defmacro make-fn-macro-unquote [name]
(spyxx name)
`(defn ~name [] 42))
(defn make-fn-func-quote [name2]
(spyxx name2)
(intern 'tst.demo.core (symbol name2) (fn [] 43)))
(dotest
(make-fn-macro-unquote fred)
(spyxx fred)
(is= 42 (spyx (fred)))
(let [wilma-var (make-fn-func-quote "wilma")]
(spyxx wilma-var)
(is= 43 (spyx (wilma-var)))))
Look at the output:
name => clojure.lang.Symbol->fred
fred => tst.demo.core$fn__38817$fred__38818->#object[tst.demo.core$fn__38817$fred__38818 0x5f832a1 "tst.demo.core$fn__38817$fred__38818#5f832a1"]
(fred) => 42
name2 => java.lang.String->"wilma"
wilma-var => clojure.lang.Var->#'tst.demo.core/wilma
(wilma-var) => 43
Note that fred is a clojure function, while wilma-var is a clojure var. Please see this post on the relationship between a symbol like foo and a var and a function.
Note also that the macro version takes an unquoted symbol fred as input, while the function version takes a plain string in double-quotes as input.
So fred is a symbol pointing to a function, while wilma-var is a symbol pointing to a var (which then points to a function). In either case, Clojure allows us to type (fred) or (wilma-var) to make a function call, and we get either 42 or 43 as a result.
Is it possible to bind the this context in ClojureScript?
Right now, I am stuck passing in this to a higher-order function, as in:
(defn generateTransactFunction [this]
(fn [item] (do stuff with this and item)))
This does not feel optimal! I am just learning ClojureScript, so I assume there's something I am missing.
EDIT:
Looks like partial can do the job, as in:
(defn abc [this arg1 arg2] ())
and passing
(partial abc this)
As you mentioned you can use partial if your this parameter at the beginning of the function's parameter list and remaining params will be bound later.
For cases where your this argument position prevents you from using partial you can use anonymous function literal which will be more consise than function literal ((fn [args...] body)):
(defn abc [arg1 arg2 this] ...)
(do-sth #(abc %1 %2 this))
Post updated to make it relevant to the course of events (responses and eliminating clutter).
Thank you very much for your time and help !
In some previous version of Clojure every var could be bound with a "binding" form.
Nowadays you get "Can't dynamically bind non-dynamic var" if not defined as dynamic.
In some contexts making a function var dynamic after definition could be useful (stubbing/mocking in testing).
Don't try:
(def ^{:dynamic true} log-call #'log-call)
It will eventually cause StackOverflowError since you are defining a function that calls itself (thank you for your explanation).
The updated question:
The approach suggested does not seem to work.
Forms called from the binding form don't get the binding defined.
Could you please help to figure out what I'm missing??
Here is the updated code:
(def all-expenses [{:amount 33.0 :date "today"}
{:amount 44.0 :date "yesterday"}])
(defn fetch-all-expenses [])
(defn fetch-expenses-greater-than [threshold]
(let [all (fetch-all-expenses)]
;calling from a nested form does not see the dynamically bound definition!
(println "2) from fetch-expenses-greater-than:" (fetch-all-expenses))
all))
(defn wrap [f]
(fn [& args] (apply f args)))
(def ^{:dynamic true} fetch-all-expenses (wrap fetch-all-expenses))
(binding [fetch-all-expenses (constantly all-expenses)]
(let [filtered (fetch-expenses-greater-than 15.0)]
(println "1) from inside binding:" (fetch-all-expenses))));calling from binding form OK!
The result of executing the in the repl is:
2) from fetch-expenses-greater-than: nil
1) from inside binding: [{:date today, :amount 33.0} {:date yesterday, :amount 44.0}]
nil
If I change the definition of fetch-all-expenses to
(defn ^:dynamic fetch-all-expenses [])
The result is as expected:
2) from fetch-expenses-greater-than: [{:date today, :amount 33.0} {:date yesterday, :amount 44.0}]
1) from inside binding: [{:date today, :amount 33.0} {:date yesterday, :amount 44.0}]
nil
It is possible to make a Var dynamic after it's been defined, but this will have no effect on code compiled before this change (it will still use the root binding). Use with-redefs to install custom replacements for functions during tests and the like.
The reason for this is that whether a Var is marked dynamic or not determines the way code using this Var is compiled. If it is not dynamic, such code will just get the root binding directly, saving some work; if it is dynamic, it will go through the somewhat more complex process of checking whether there is a thread-local binding in place for it.
So, there is no way to cause already compiled code use a custom function installed with binding after marking the Var holding the function dynamic. However, those calls still go through the Var, they just happen to go directly to the root binding, so you can use custom replacement functions for testing and the like if you install them as root bindings for the appropriate Vars. with-redefs encapsulates all the necessary logic for doing this cleanly.
Let's see how this works at the REPL:
;; define a non-dynamic Var:
(def foo 0)
;; this will throw an exception complaining about the attempt
;; to bind a non-dynamic Var:
(binding [foo 1]
foo)
;; let's define a function using foo;
;; we'll use it further down:
(defn bar []
foo)
;; now let's mark the Var dynamic:
(.setDynamic #'foo)
;; this will now evaluate to 1:
(binding [foo 1]
foo)
;; however, this will still return 0:
(binding [foo 1]
(bar))
(def ^{:dynamic true} log-call #'log-call) This statement says: "Create a var log-call and bind it to the var log-call. So when you try to use log-call var it will keep refering itself forever and hence StackOverflow exception.
You can try something like this:
(defn wrap [f]
(fn [& args] (apply f args)))
(def ^{:dynamic true} log-call (wrap log-call))
(def ^{:dynamic true} fetch-all-expenses (wrap fetch-all-expenses))
Thank you very much for your answer #MichaĆMarczyk. That explains it.
With code using the var before making it dynamic:
(def foo 0)
(defn bar []
foo)
(.setDynamic #'foo)
(binding [foo 1]
;; prints foo 1 . bar 0
(println "foo" foo ". bar" (bar)))
With code using the var after making it dynamic:
(def foo 0)
(.setDynamic #'foo)
(defn bar []
foo)
(binding [foo 1]
;; prints foo 1 . bar 1
(println "foo" foo ". bar" (bar)))
And yes!....with with-redefs instead of binding everything works as expected. That's exactly what I was needing.