Skip to content

Instantly share code, notes, and snippets.

@bendlas
Last active October 1, 2015 05:39
Show Gist options
  • Save bendlas/fd866536f4b0a0f7dcd4 to your computer and use it in GitHub Desktop.
Save bendlas/fd866536f4b0a0f7dcd4 to your computer and use it in GitHub Desktop.
On source code configuration in the clojure family of languages

statement of issue

The current requirement for defining a data_readers.clj at the source-path root makes creating libraries, that define reader-tags, cumbersome:

  • Users need to paste the library reader-tags into their data_readers.clj
  • Maintainers won't want to use reader tags in the library's own source, because users might forget to paste them
  • Shipping a data_readers.clj (like datomic does) bears the risk of libraries randomly clobbering each other (see lein-collisions)

Also other interactions arount reader tags could be improved:

  • There is no clear way to reuse a safe subset of source data_readers as a default for the edn reader ...
  • ... or even overriding them locally for a source-file/namespace

thoughts on a solution

data_readers (or a similar mechanism) need to be namespaced similar to source files. Here is a sketch proposal for how a __readers__.edn file could be designed to alleviate the problems with data_readers

namespacing of files

A __readers__ file should be in a globally project-unique subfolder. Each __readers__ file may define reader-tags that are namespaced to its containing namespace folder + they may define tags in sub-namespaces. Overriding tags from parent namespaces is permitted, but should stay compatible if at all possible.

shorthanding

Each __readers__ file may propose a global shorthand alias with the ^tag-sym metadata. For /datomic/__readers__.edn this would look like

{^db/id datomic/id datomic.db-id-literal ...}

If the global shorthands of two tags collide, a warning is logged and the shorthand is deactivated.

externalized configuration a'la data_readers

Imagine an extension to require, where, before loading the source file, it would look for files named like #"<ns_name>.ns.clj[cs]?" along its namespace-parent chain, to prepare the source reading environment.

So (require 'datomic.api) would confgure the environment setup with [#path[datomic api.ns.cljc], #path[datomic.ns.cljc]] before attempting to read #"/datomic/api.clj[cs}?". Since, in the case of datomic that won't happen, because, as with other AOT compiled code, the runtime will only load the .class files, we need to discuss ...

... whether effects of a *.ns.cljc file need to be confined to its [sub-]namespace[s], hence comforably be a require(/ compile / read)-time only concept (in this case, I'd rather be inclined to call it *.ns.edn), or

... whether the config is reified (hence registered by loading the .class file) so that it may serve any application-specific purpose, like sharing a set of safe readers between the repl and data.edn.

... or whether these need to be two different files

This could also help with other namespace-setup issues:

  • default requires: I'll just say [clojure.string :as str] [clojure.tools.logging :as log].
  • different namespaces offering similar api: cljs.core.async vs clojure.core.async

Basically it could customize the template for ns-clauses

Q/A

  • Q Isn't the data format for this configuration hard to agree on?
    • A Yes, so we should just base it on the existing var-names clojure.{core,repl,reader}, where applicable, but there is some naming to be done like the reader-tag 'db/id in a clj context vs ... in a cljs context
  • Q Doesn't splitting off the config destroy the coherence of the file?
    • A Not more so than most splitting. Try loading random files in random project without having their companions on the classpath.
  • Q Still, at least reading should be simple.
    • A Fully boxed reading, with reified read-cond and unresolved tags, will always be there for you.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment