I would like to enable users of the Cryogen static blog generator to override selected functions (in its compiler ns) so that they can for example provide extra data to the page being rendered. How to make this possible?
1. binding
The simplest solution is to use binding to ad-hoc override the functions of interest:
however, as former OOP dev, it seems “dirty” to override random functions somewhere deep in a function call though it also looks as the simplest solution.
2. protocols
The standard Clojure solution for supporting alternative implementations is protocols - I could modify the root function to take an implementation of a new Compiler protocol. That is nice and clean - but the problem is that there are potentially many functions of interest but I only want to override a single one.
I could solve that by making my implementation delegate all other functions to the original impl but it still forces me to declare that for each:
They are way too flexible, I do not need so much flexibility. But it requires the least ceremony it seems, supporting selective overriding, if we leverage :default and a single other dispatch value:
(defmulti compile-tags-page (constantly :custom))
(defmethod compile-tags-page :default [..] ...)
;; If I want to override it, I just do:
(defmethod compile-tags-page :custom [..] (do-my-custom-stuff))
Clojure 1.10 added an :extend-via-metadata flag. You would implement the protocol normally (but with :extend-via-metadata true set) and provide a factory function for an object with the default implementations. Users can override a specific function with
The function is an action that produces html from a template and site data such as tags info. It is this fn that passes the data to the template renderer so if I want to pass it more data I must change it. Even if not, I’d need to change another function, that generates the data (based on the site resources). So the need to override stays. But you have a good point, I need to think broader and see whether I can redesign it somehow.
Firstly, there’s nothing wrong with binding. That said, some other ideas include:
pass a map whose keys are things like :compile-tags-page and values are the various functions of interest, which would allow a user of the library to merge a map of their own overrides
pass a namespace, which can be treated the same way as the above-mentioned map (vars act as key/value pairs)