Better cloning?
Opened this issue · 2 comments
We have a really cool semantic-csv.impl.core/clone-var
macro that expands to a def which refers to the original var, but copied over the docstring, argslist and everything. I don't have the patience right now to get something that does "the best we can" in making this cross compatible with cljs, because portable cljs macros are nutty af. I did a little work in this direction though, and here are some of the things I've come up with:
This would maybe be the most conservative thing, and seems to work for the clj side at least. I don't yet know however if the cljs part works (either ClojureScript JVM or ClojureScript JS). There's a good chance it may not for the reasons mentioned in the portable cljs blog post (the reader cond is expanding at macro compile time, so thing may not work quite right there for JVM targeting JS, but JS targeting JS might).
(defmacro clone-var
"Clone the var pointed to by fsym into current ns such that arglists, name and doc metadata are preserned."
[fsym]
#?(:clj
(let [v (resolve fsym)
m (subset-map (meta v) [:arglists :name :doc])
m (update m :arglists (fn [arglists] (list 'quote arglists)))]
`(def ~(vary-meta (:name m) (constantly m)) ~fsym))
:cljs
`(def ~(symbol (name fsym)) ~fsym)))
There's also the following, which tries to use the tricks from the above blog post, but it isn't even working for clj as is. And the blog post is kind of focused on a particular part of the general problem which is getting the vars to resolve properly.
(defmacro clone-var
"Clone the var pointed to by fsym into current ns such that arglists, name and doc metadata are preserned."
[fsym]
`(if-cljs
~(let [v (resolve fsym)
m (subset-map (meta v) [:arglists :name :doc])
m (update m :arglists (fn [arglists] (list 'quote arglists)))]
`(def ~(vary-meta (:name m) (constantly m)) ~fsym))
`(def ~(symbol (name fsym)) ~fsym)))
I'm not really interested in optimizing this any more until we get some cljs testing set up with doo or whatever (see #56). For now I'm just going to do a top level reader conditional that decides whether to manually call (def the-var path.to.actual/the-var)
a bunch of times, or use the macro. The only downside is loosing docstrings for cljs, which isn't the end of the world.
(Relates to #45)
One of the tricks for this is going to deal with var resolution. Cljs does not have a resolve
function. However, cljs.analyzer.api
does have a resolve. But it returns an analyzer map, which is good for cljs, but it will lead to a different implementation in cljs. It may just be best to implement this method seperately for cljs to solve to bootstrapped cljs issue.
It would be cool if we could wrap them up into one macro so that we can point people to it (I know I've wanted this macro before). But either way is fine really.