Fork me on GitHub

Announcing , a safer alternative to Potemkin's import-vars ! expose-api : A Clojure library to automatically generate public API namespaces by wrapping and exposing functions and macros from implementation namespaces. Features: • Generate a public API namespace from a specified namespace declaration and a list of implementation vars. • Maintain consistent documentation and arities for the exposed functions and macros. • Generates Clojure source code, leaving the runtime in a clean state.

👀 6
🎉 8
👍 2
❤️ 1

Cool! So, generating source code instead of generating vars at load time, right? After getting fed up with the nth mystery caused by Potemkin import-vars, I did something along these lines for rewrite-clj a while back. I expect your solution to be much more polished than my little script!


That's correct. It gets the doc-string and arg-vectors of the vars from the impl namespaces you want to expose, and generates a defn or defmacro in the API namespace you want generated with the same arity and argument names, and the same doc-string, which calls into the implementation. Defns are inlined as well. I think there's some limitations currently, mostly I don't think it works with destructed arguments, but it supports var-arg. And you still have to write the ns form yourself for now. It avoids the issues with Potemkin, and also it lets lsp and clj-kondo see the arities, arg names, and the doc-strings, so if you don't have a REPL you still get those to show you the doc and all that.


I think something like this would be super useful for polylith. I must admit that the polylith interface-ns reminds me a bit too much of enterprise Java, and would rather have a poly/definterface or your expose-api than manually maintaining an interface ns which just delegates to the impl ns

👍 2

Mandatory, build-time code generation is a bit of a departure from what Lisp offers, but I too find the approach of just making wrappers instead of doing screwy value tracking magic like potemkin does to be a better idea.


interesting! tried to replace potemkin in with expose-api, wrote two issues out of it to expose-api.

👍 1

> Mandatory, build-time code generation is a bit of a departure from what Lisp offers, but I too find the approach of just making wrappers instead of doing screwy value tracking magic like potemkin does to be a better idea. Potemkin's approach is just excessively odd, making a var that mirrors another var is a trivial defmacro to write for anyone knowing how Clojure vars and metadata work (with that said, good work @U0K064KQV - good to have choices)


Ya, actually, as this conversation unfolded, I thought. I could make the same thing as a macro. Look up the doc/args meta, and the macro meta, and then define a wrapper defn or defmacro in the current namespace that calls the other. It would work better than potemkin's import-vars, since it's not trying to re-intern existing vars and all that. But it wouldn't allow static tools to understand what's going on, and a user couldn't just look up the source either to see what the "API" looks like with doc/arities. So I still prefer the code-gen approach overall.


Yes, that's one controversial point for me, I'm biased but I just code it the 'lisp' way and if a particular set of tools can't understand it, that would be a nudge for them to be able to do so eventually Not like it's written in stone that e.g. clojure-lsp will always be 100% static, both sides would enjoy a mixture (and people like @U0ETXRFEW are pioneering such hybrids calva)


I might think of adding that macro as well, so users can choose to code-gen or use the macro.


If I've understood correctly, the idea of importing vars at load time is unpopular in the Clojure community. I personally don't have an issue with the concept, but if some technique is unpopular, then I feel it is best to go with the flow and avoid spending time on edge cases and mysteries.


Also, our favourite static analyzer, clj-kondo, won't see these dynamically loaded variables (unless you write some clj-kondo hooks).


Having those hooks written would be a good state-of-the-art advancement, probably it's one of those situations where everyone is waiting for someone else to actually do it 😄 I've never written a hook, I'm occasionally interested although they look like dark magic to me


clj-kondo hooks are a bit similar to macros, I’d say. I have very limited experience with them, but anyway.


@U45T93RA6 I added in 0.3.0 expose-vars macro if you prefer that over code-gen. I didn't document it on readme yet, but the macro has a nice docstring:

🙌 1