Is there a way in clojure to get a function's code after the function has been loaded?
Ie. without doing something like [untested]
(defmacro blat [x] `(do (def code ~(quote (mexpand-all x)))
~x)))
(blat (defn func [abc] (...)))
You can get the source of a symbol using the clojure.repl/source function. However, this only works if the var for which the symbol resolves to is in a .clj file on the classpath. You can't, for example, do this:
user=> (defn foo [x] x)
#'user/foo
user=> (require 'clojure.repl)
nil
user=> (clojure.repl/source foo)
Source not found
nil
Related
Given the following function
(defn func []
(break!))
I want the break! function to start a repl, and inside that repl I can print the callstacks that invokes func.
The feature is like ipdb in Python, which comes quite handy when we want to interactively investigate inside the runtime of some function.
A very basic version could just be:
(defn break! [] (clojure.main/repl))
That will start a new, nested REPL, and when you exit it (with ^D -- or ^Z on Windows), it will continue on.
You can't evaluate locals, but if you write break! as a macro, you can at least display the active locals before starting the REPL:
user=> (defmacro break! []
(let [locals (into {} (map (juxt keyword identity)) (keys &env))]
(prn locals)
(clojure.main/repl :prompt #(print "nested> "))))
#'user/break!
user=> (defn foo "Using the break! function." [a b] (break!))
#'user/foo
user=> (foo 13 42)
{:a 13, :b 42}
nested> (doc foo)
-------------------------
user/foo
([a b])
Using the break! function.
nil
nested> ^D
nil
user=>
If you want the stacktrace as data inside the nested REPL, you can evaluate (:trace (Throwable->map (Throwable.)))
I want to extend and redefine a clojure.core macro. For example, how can I redefine clojure.core/defn while using the original definition?
This is a bit tricky, but by aliasing the macro in clojure.core this is possible.
Open the repl and follow the steps below.
➜ ~ clj
Clojure 1.9.0
First alias clojure.core/defn to something else:
user=> (in-ns 'clojure.core)
#object[clojure.lang.Namespace 0x48e92c5c "clojure.core"]
clojure.core=> (defmacro defn-holder [])
#'clojure.core/defn-holder
clojure.core=> (alter-var-root #'defn-holder (constantly (var-get #'defn)))
#object[clojure.core$defn__5154 0xd3957fe "clojure.core$defn__5154#d3957fe"]
Next, create a new defn macro that uses the alias definition
clojure.core=> (in-ns 'user)
#object[clojure.lang.Namespace 0x64ba3208 "user"]
user=> (defmacro defn [& args] `(do (println "aliased version")(clojure.core/defn-holder ~#args)))
WARNING: defn already refers to: #'clojure.core/defn in namespace: user, being replaced by: #'user/defn
#'user/defn
user=> (defn foo [a])
aliased version
#'user/foo
user=> (foo 1)
nil
However it doesn't work for all namespaces yet:
user=> (ns bar)
nil
bar=> (defn foo [a])
#'bar/foo
We need to redefine defn in clojure.core with our new definition:
bar=> (in-ns 'clojure.core)
#object[clojure.lang.Namespace 0x48e92c5c "clojure.core"]
clojure.core=> (alter-var-root #'defn (constantly (var-get #'user/defn)))
#object[user$defn 0x37052337 "user$defn#37052337"]
Now it works:
clojure.core=> (in-ns 'bar)
#object[clojure.lang.Namespace 0x37efd131 "bar"]
bar=> (defn foo [a])
aliased version
#'bar/foo
In clojure, can one idiomatically obtain a function's name inside of its body, hopefully accomplishing so without introducing a new wrapper for the function's definition? can one also access the function's name inside of the body of the function's :test attribute as well?
For motivation, this can be helpful for certain logging situations, as well as for keeping the body of :test oblivious to changes to the name of the function which it is supplied for.
A short elucidation of the closest that meta gets follows; there's no this notion to supply to meta, as far as I know, in clojure.
(defn a [] (:name (meta (var a))))
Obviously it is easy to accomplish with a wrapper macro.
Edit: luckily no one so far mentioned lambda combinators.
There are 2 ways to approach your question. However, I suspect that to fully automate what you want to do, you would need to define your own custom defn replacement/wrapper.
The first thing to realize is that all functions are anonymous. When we type:
(defn hello [] (println "hi"))
we are really typing:
(def hello (fn [] (println "hi"))
we are creating a symbol hello that points to an anonymous var which in turn points to an anonymous function. However, we can give the function an "internal name" like so:
(def hello (fn fn-hello [] (println "hi")))
So now we can access the function from the outside via hello or from the inside using either hello of fn-hello symbols (please don't ever use hello in both locations or you create a lot of confusion...even though it is legal).
I frequently use the fn-hello method in (otherwise) anonymous functions since any exceptions thrown will include the fn-hello symbol which makes tracking down the source of the problem much easier (the line number of the error is often missing from the stack trace). For example when using Instaparse we need a map of anonymous transform functions like:
{
:identifier fn-identifier
:string fn-string
:integer (fn fn-integer [arg] [:integer (java.lang.Integer. arg)])
:boolean (fn fn-boolean [arg] [:boolean (java.lang.Boolean. arg)])
:namespace (fn fn-namespace [arg] [:namespace arg])
:prefix (fn fn-prefix [arg] [:prefix arg])
:organization (fn fn-organization [arg] [:organization arg])
:contact (fn fn-contact [arg] [:contact arg])
:description (fn fn-description [arg] [:description arg])
:presence (fn fn-presence [arg] [:presence arg])
:revision (fn fn-revision [& args] (prepend :revision args))
:iso-date (fn fn-iso-date [& args] [:iso-date (str/join args)])
:reference (fn fn-reference [arg] [:reference arg])
:identity (fn fn-identity [& args] (prepend :identity args))
:typedef (fn fn-typedef [& args] (prepend :typedef args))
:container (fn fn-container [& args] (prepend :container args))
:rpc (fn fn-rpc [& args] (prepend :rpc args))
:input (fn fn-input [& args] (prepend :input args))
...<snip>...
}
and giving each function the "internal name" makes debugging much, much easier. Perhaps this would be unnecessary if Clojure had better error messages, but that is a longstanding (& so far unfullfilled) wish.
You can find more details here: https://clojure.org/reference/special_forms#fn
If you read closely, it claims that (defn foo [x] ...) expands into
(def foo (fn foo [x] ...))
although you may need to experiment to see if this has already solved the use-case you are seeking. It works either way as seen in this example where we explicitly avoid the inner fn-fact name:
(def fact (fn [x] ; fn-fact omitted here
(if (zero? x)
1
(* x (fact (dec x))))))
(fact 4) => 24
This version also works:
(def fact (fn fn-fact [x]
(if (zero? x)
1
(* x (fn-fact (dec x))))))
(fact 4) => 24
(fn-fact 4) => Unable to resolve symbol: fn-fact
So we see that the "internal name" fn-fact is hidden inside the function and is invisible from the outside.
A 2nd approach, if using a macro, is to use the &form global data to access the line number from the source code. In the Tupelo library this technique is used to improve error messages for the
(defmacro dotest [& body] ; #todo README & tests
(let [test-name-sym (symbol (str "test-line-" (:line (meta &form))))]
`(clojure.test/deftest ~test-name-sym ~#body)))
This convenience macro allows the use of unit tests like:
(dotest
(is (= 3 (inc 2))))
which evalutes to
(deftest test-line-123 ; assuming this is on line 123 in source file
(is (= 3 (inc 2))))
instead of manually typing
(deftest t-addition
(is (= 3 (inc 2))))
You can access (:line (meta &form)) and other information in any macro which can make your error messages and/or Exceptions much more informative to the poor reader trying to debug a problem.
Besides the above macro wrapper example, another (more involved) example of the same technique can be seen in the Plumatic Schema library, where they wrap clojure.core/defn with an extended version.
You may also wish to view this question for clarification on how Clojure uses the "anonymous" var as an intermediary between a symbol and a function: When to use a Var instead of a function?
Let's take that Clojure code:
(defn ^{:test-1 "meta-test-1"} fn-key-1
[x]
(eval nil))
(defn ^{:test-2 "meta-test-2"} fn-key-2
[x]
(eval nil))
(def some-map {fn-key-1 "test-1"
fn-key-2 "test-2"})
As you can see, the keys of my map are Symbols that refers to functions. I don't thing there is anything special there.
As you can see, when defining the map the keys of my map are Symbols that refers to functions. However, when they are read by the reader, then they get resolved to the function objects.
Now, what I want to do is to iterate over my some-map map to get the meta-data for each of the keys.
I was thinking doing this that way:
(defn some-fn
[m]
(doseq [[fn-key value] m]
(println (meta fn-key))))
However, what is being printed here is nil. This is expected since the meta-data is defining the symbol, and not the function. So, there is no meta-data attached to the function and it is why nil is being returned/printed.
So this lead to a question: is it possible to get the symbol that refers to my function "fn-key" in that context?
In that doseq loop, it appears that fn-key is a function and not the symbol of that function. What I need is a way to get the symbol of that function such that I can use (meta (get-symbol fn-key)).
Question Resolution
This question got resolved by defining the functions that way:
(def fn-key-1
(with-meta (fn [x] (eval nil)) {:foo "bar"}))
(def fn-key-2
(with-meta (fn [x] (eval nil)) {:foo "bar"}))
Revision of the Resolution
I revised the solution above to make it cleaner. In fact, if you were to type fn-key-1 in the REPL, you were to get an anonymous function reference such as #< clojure.lang.AFunction$1#15ab1764>. The problem with that is that it make things difficult to debug when you don't know what is being evaluated. To solve this issue, I changed the two function definitions to use this syntax, which resolve this issue:
(def fn-key-1 ^{:foo "bar"} (fn [x] (eval nil)))
(def fn-key-2 ^{:foo "bar"} (fn [x] (eval nil)))
Now, if we type fn-key-1 then we will get something like #<core$foo user.core$foo#66e37466> instead. At least, we have the namespace & symbol signature of this function object.
(def some-map {#'fn-key-1 "test-1"
#'fn-key-2 "test 2"})
fn-key-1 is resolved by the reader to the function object (which has no metadata to query). #'fn-key-1 resolves to the var itself, and holds the metadata.
I'm looking for the ability to have the REPL print the current definition of a function. Is there any way to do this?
For example, given:
(defn foo [] (if true "true"))
I'd like to say something like
(print-definition foo)
and get something along the lines of
(foo [] (if true "true"))
printed.
An alternative to source (which should be available via clojure.repl/source when starting a REPL, as of 1.2.0. If you're working with 1.1.0 or lower, source is in clojure.contrib.repl-utils.), for REPL use, instead of looking at functions defined in a .clj file:
(defmacro defsource
"Similar to clojure.core/defn, but saves the function's definition in the var's
:source meta-data."
{:arglists (:arglists (meta (var defn)))}
[fn-name & defn-stuff]
`(do (defn ~fn-name ~#defn-stuff)
(alter-meta! (var ~fn-name) assoc :source (quote ~&form))
(var ~fn-name)))
(defsource foo [a b] (+ a b))
(:source (meta #'foo))
;; => (defsource foo [a b] (+ a b))
A simple print-definition:
(defn print-definition [v]
(:source (meta v)))
(print-definition #'foo)
#' is just a reader macro, expanding from #'foo to (var foo):
(macroexpand '#'reduce)
;; => (var reduce)
You'll want to import the repl namespace, and use the source function from it:
(ns myns
(:use [clojure.repl :only (source)]))
(defn foo [] (if true "true"))
(source foo)
=> (foo [] (if true "true"))
nil
Though this wouldn't work in the REPL, only where the function is defined in a .clj file on the classpath. Which doesn't answer your question, then: you'd need to have a defn that stores, in the metadata of the fn it defines, the source of the function. Then you'd write a function that recalls that bit of metadata. That shouldn't be terribly difficult.
Clojure doesn't have a decompiler, so that means there's no way to get at the source of an arbitrary function unless it was a defn loaded from disk. However, you can use a neat hack called serializable-fn to create a function that has its source form stored in its metadata: http://github.com/Seajure/serializable-fn
The defsource answer is very similar to this, but this solution works with arbitrary fns, not just top-level defns. It also makes fns print prettily at the repl without a special printing function.
In clojure 1.2's REPL, the source function is immediately available. You can use it this way:
$ java -cp clojure.jar clojure.main
Clojure 1.2.0
user=> (source slurp)
(defn slurp
"Reads the file named by f using the encoding enc into a string
and returns it."
{:added "1.0"}
([f & opts]
(let [opts (normalize-slurp-opts opts)
sb (StringBuilder.)]
(with-open [#^java.io.Reader r (apply jio/reader f opts)]
(loop [c (.read r)]
(if (neg? c)
(str sb)
(do
(.append sb (char c))
(recur (.read r)))))))))
nil
user=>
A few other functions are also automatically imported into the REPL's user namespace from the clojure.repl library. See the API doc here.
However, as pointed out in other answers here, you can't use source as is to print back functions you have defined in the REPL.
I asked exactly this question on the Clojure mailing list recently and the answers included overriding parts of the REPL to stash the input (and output) away for future reference as well as an override of defn to store the source in metadata (which you could then easily retrieve in the REPL).
Read the thread on the Clojure mailing list