Skip to content

Instantly share code, notes, and snippets.

@mrzor
Created October 15, 2020 11:05
Show Gist options
  • Save mrzor/6b700a807710fbcec4a88a2ed339b5bf to your computer and use it in GitHub Desktop.
Save mrzor/6b700a807710fbcec4a88a2ed339b5bf to your computer and use it in GitHub Desktop.
Integrant Doc

First immediate issue I see is that we don't have a common definition for "component" in the context of Integrant. The README only alludes to the idea in the read-namespaces doc. All other uses point to the eponymous library. Integrant operates at the "config" level. Might be a component - doesn't have to be.

I understand such an Integrant "component" to be some set of Integrant keywords - let's call that an ig/key. An ig/key exists is a key in a configuration map, and init-key is provided for this keyword (optionally, more methods, as you well know). In my project, I call such sets of Integrant keywords "subsystems" and they are defined by .. a subsystem function that returns a subsystem map. These subsystem maps are (merge ...'d together to form a "system" map. As it happens, in my case and maybe in yours, all keys of a given subsystem are namespaced keywords that share a namespace - so maybe that's a connected, somewhat equivalent definition of an Integrant component. All of this is in line with README terminology - forgive me for "stating the obvious" here.

Subsystem map metadata feels like a good place to document a subsystem. If, like me, you have functions generating such subsystem maps, then usual function documentation should work just fine. If you don't, documenting the var holding the config map may work still.

For "task" (loosely defined as "something to be achieved using several components") spanning multiple components, it gets muddier. It comes down to how such "tasks" are used in practice - because afaict there's no such notion provided by Integrant. I believe they can be presented as higher-order subsystems, using ig/refs to regular subsystems, as a way of packaging/demonstrating such a task. Once again, if a task is generated by a function and not just placed in a var, we have opportunities for documentation. Even more so if these functions accept arities beyond zero - hinting at the possibility of customization.

Finally, ig/key documentation. There is no such thing in the general Clojure ecosystem afaict. The usual practice is to document namespaces, vars and functions. There is an exception in with clojure.spec.alpha though, that leverages the singleton registry that clojure.spec relies on. Sourcecode review of clojure.repl/doc provides some insight into this.

Integrant has some spec support, and because config keys can mean - and resolve - to different things in different contexts, this support relies on an additional multimethod. Perhaps key-level documentation could be based on an additional multimethod, let's call it ig/doc-key. I'm not entirely sure how to call ig/doc-key on a (sub)system map to actually collect the documentation outputs, but integrant.core/fold and integrant.core/run! look promising. Whoever calls ig/doc-key should call spec/describe whenever a spec is found for that key, in a way comparable to what repl/print-doc does.

I'm leaving aside the idea of adding a special '(:ig/doc "xxx") k/v to the config map associated with the ig/key. I'll admit this is place-oriented thinking, but I still find bags of conceptually heterogenous things to be confusing. Nevermind the added (dissoc ... everywhere you would otherwise use the key config wholesale.


I reviewed all of the documentation tools listed on the Clojure Toolbox, and none of them allow you to provide a "selector function" to determine what should or should not be documented. Most of them allow selection to take place on a namespace or file basis, which I don't believe would work to document a set of Integrant "components" spread all over the place.

Now to be honest, I'm not too sure what a Clojure documentation tool should make of a set of Integrant components. Maybe it's best to generate something simple (i.e. markdown) at first and evolve from there.


I'm afraid that as things stand, and because Integrant does not specify how systems should be built beyond the system map, it would be quite hard to devise a documentation tool that would not be prescriptive w.r.t. how a larger codebase would be structured. I'm fairly sure that exposing functions instead of vars is desirable for the added expressivity.

TL; DR; In your position, I would roll out my own thing that would take advantage of how your codebase is structured. I would take the opportunity to establish some conventions on that topic if possible, or at least start a conversation with the team.

Using metadata at each level of abstraction looks the most Clojuresque way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment