This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-10-03
Channels
- # babashka (16)
- # beginners (70)
- # biff (1)
- # calva (15)
- # clj-commons (1)
- # cljsrn (1)
- # clojure (31)
- # clojure-android (1)
- # clojure-nl (4)
- # clojure-spec (17)
- # clojurescript (6)
- # cursive (3)
- # fulcro (22)
- # holy-lambda (21)
- # honeysql (20)
- # jobs-discuss (5)
- # kaocha (1)
- # lsp (51)
- # malli (1)
- # missionary (7)
- # nrepl (1)
- # off-topic (9)
- # portal (3)
- # reitit (1)
- # shadow-cljs (4)
- # xtdb (2)
Can I redefine "defn", so it stores the form past to it in metadata ?
another option is to use the existing meta to look up the function. There's a built in function clojure.repl/source
, but it is a little inconsistent. I adapted source
to work more consistently, which you can see at:
https://github.com/phronmophobic/reveal-exception/blob/main/src/com/phronemophobic/reveal_plugin/reveal_exception.clj#L83
yes, I did use that. My use case goes into "diffing" of clojure source code, but per function (instead of being file based) So I thought it could be interesting to look at the form which defined a function.
how could I do that ?
without getting in a circle ?
are you diffing between versions in version control or redefined at the repl?
hmmm. I want to track changes in the definition of functions over time. They are defined in source files. So the source-fn approach works in principle.
Just a pitty that it gives me a String back.
So I read somewhere te idea to change the defn macro, to store the form as metdata.
it's pretty easy to get a data structure from the string, you can just use read
or read-string
Lets give it a try.
are you tracking changes while someone is working at the repl or some other way?
if you're trying to track changes at the repl, I might go the route of writing some repl middleware that receives each expression that will be eval
'd
if you're tracking changes in source files, I would use a tool like rewrite-clj
that can parse clojure source into an AST
the source code -> read-string is fine,., probbaly
and the file parsing is as well a good idea.
thanks
also check out https://clojure-lsp.io/ which has great static analysis tools that might be helpful
(defmacro defn
[& body]
`(doto
(clojure.core/defn ~@body)
(alter-meta!
merge
{:form '~&form})))
You can try it with:
(defn foobar
"This is a doc"
{:some-attr :a1}
([] (foobar 1 2 3))
([a b c]
{:pre [(number? a) (number? b) (number? c)]}
(+ a b c))
{:some-other-attribute :a2})
(:form (meta #'foobar))
It preserves all features of defn, and it should work even when AOT compiled I believe.
I got a good start with doing this:
(defmacro defn* [x & body]
(let [form `'~&form
x (vary-meta x assoc :form form)]
`(def ~x (fn ~@body))))
(intern 'clojure.core
(with-meta 'defn {:macro true})
@#'defn*)
(defn sub [a b] (- a b))
(:form
(meta #'sub))
;; => (defn sub [a b] (- a b))
and it works for simple "defn". But require a more complex ns after this fails.
probably my defn* macro is too simple to work with all types of defn invocations in real code.
To substitute it is a bit tricker. You need to grab the Var of the real defn and save it somewhere. Then ns-unmap from clojure.core defn, and then def inside clojure.core your new defn, and switch your defn to call the var you saved of the original. I think that should work, though I've never tried to substitute a core macro that also called the original, I only did it with functions which are a bit simpler cause you can swap the var to point to the new function.