views:

53

answers:

1

I am writing a Clojure library and I am wondering what is the best practice for setting configuration parameters of a library.

Many libraries (like those in clojure-contrib) use global level parameter like *buffer-size* which the user can set by calling set! on them. But this does not seems to be best way to me as it creates a global state and there are chances of name collision.

The other way is to pass the parameters in every function call that is dependent on them. If there are many parameters then a map of them can be used instead of passing individual ones.

As an example, let's say that I am writing a cache library.

Using the first approach I have global parameters like *cache-size*, *expiry-time*, *cache-dir* etc. The user set!s these (or not and let them be default) and calls functions like (set-in-cache id obj) and (get-from-cache id).

Using the second approach, the user first create a map of parameters and passes it to every call

(def cache-parameters {:cache-size 1000 
                       :expiry-time: 1440 
                       :cache-dir "c:\\cache"})
(set-in-cache cache-parameters id obj)
(get-from-cache cache-parameters id)

So which way is the preferred way in Clojure and why?

+3  A: 

Actually you cannot set! things like c.c.io's *buffer-size* unless you install a thread-local binding for them with binding, with-bindings etc. There's just a handful of Vars for which thread-local bindings are installed by lower level Clojure machinery, such as *warn-on-reflection* and *read-eval*, making them set!-able at top-level; user-defined Vars are not set!-able at top-level. The root binding of a Var can be changed with e.g. alter-var-root, intern, def, .bindRoot ..., but this should be used sparingly.

As for the rebindable Vars vs. explicit parameters part of the question: going with explicit parameters is almost always ok and usually preferable, just because of the increased maintainability of functions which clearly display all pieces of data they depend on. That being said, if some piece of configuration is likely to be set once, then used by virtually every function call within the app / library ever after, it might make for saner code to define an earmuffed Var, document it well and put the config in it (and this might be one of the rare cases where changing a Var's root binding outside of the form which defines might be ok).

To summarise, use your best judgement, if unsure -- err on the side of explicit parameter passing.

Michał Marczyk
That last bit is *exactly* what I did in clj-github and gotmilk. Awesome to know I might have did something right!
Rayne
`it might make for saner code to define an earmuffed Var ...`Can you give an example?
abhin4v
@Rayne: ;-) @abhin4v: Actually `*buffer-size*` which you mention in the question is a good example of using a Var to hold a "reasonable default" which might occasionally be rebound by client code. As for rebinding of a Var's root value, Congomongo (the Clojure MongoDB library) uses `alter-var-root` on a Var called `*mongo-config*` (whose purpose is as the name suggests).
Michał Marczyk
Thanks. I solved my issue by using `resolve`. The user of the library will have to `def` a particular var and my library just `resolve`s it.
abhin4v