Allow reusing of another functions `:doc` and `:arglists`
PEZ opened this issue · comments
Is your feature request related to a problem? Please describe.
function-a
takes the same arguments as function-b
. In fact, function-a
calls function-b
. Sometimes they also share docs. E.g. with Polylith style interfaces and implementations. Without too much synchronized updating of the function signatures, I'd like for clojure-lsp to show me the same docs/hover info for the two functions.
At the REPL, this works:
(defn function-b
"Prints `:a`, `:b`, and `:c` from the argment map.
* :a The a of the map
* :b The b of the map
* :c The c of the map"
[{:keys [a b c]}]
(println a b c))
(defn function-a
{:arglists (:arglists (meta #'function-b))
:doc (:doc (meta #'function-b))}
[args]
(function-b args))
It makes the sharing very clear.
(doc function-b)
will print:
-------------------------
.../function-b
([{:keys [a b c]}])
Prints `:a`, `:b`, and `:c` from the argment map.
* :a The a of the map
* :b The b of the map
* :c The c of the map
Describe the solution you'd like
It would be super nice if it worked with clojure-lsp too, making for a shared way for the dynamic and static tools to help with this.
Describe alternatives you've considered
There are some ways to make the sharing a bit less verbose than full copies of the the docs and argument lists, but I don't think none is this clear and complete and also at the same time works for the REPL.
Additional context
Here's a write-up about the general problem and some ways to deal with it with the current clojure-lsp: https://blog.agical.se/en/posts/keeping-the--arglists-of-clojure-functions-dry/
Here's a Clojurians Slack thread where that blog post started from: https://clojurians.slack.com/archives/C03S1KBA2/p1713449139604419 (It also reveals that the actual suggestion in this feature request comes from @ericdallo 😄 )
Note that function-a
as defined will fail to compile when its body contains a call to itself:
https://ask.clojure.org/index.php/13834/compiler-exception-dynamically-arglists-metadata-recursive
The interpretation given by @puredanger in the linked thread was that :arglists
in the attr-map should be restricted to literal quoted lists, so expressions to be evaluated like (:arglists (meta #'function-b))
should be at least discouraged (if not considered illegal).
To avoid this construction, one has to use alter-meta!
on the var after its creation, which I imagine is trickier to statically analyze:
(defn function-a [args]
(function-b args)
(alter-meta! #'function-a
assoc
:arglists (:arglists (meta #'function-b)))
;; or
(alter-meta! #'function-a
merge
(select-keys (meta #'function-b)
[:arglists :doc]))