22
votes
  • defn = public
  • defn- = private

Perhaps I have bad Clojure coding style -- but I find that most functions I write in Clojure are small helper functions that I do not want to expose.

Is there some configuration option, where:

  • defn = private by default,
  • and to make something public, I have to do defn+?

Thanks!

4

4 Answers

17
votes

No. There is not.

An alternative approach which might or might not work for you is to declare a foo.bar.internal namespace containing all the private helpers which is used by your foo.bar namespace. This has advantages over private function declarations when you want to use private functions in macro expansions.

6
votes

The beauty of Clojure being a Lisp, is that you can build and adapt the language to suit your needs. I highly recommend you read On Lisp, by Paul Graham. He now gives his book away for free.

Regarding your suggestion of defn+ vs defn vs defn-. This sound to me like a good case for writing your own Macro. defn is a function and defn- is a macro. You can simply redefine them as you wish, or wrap them in your own.

Here follows a suggestion to implementation, based mainly on Clojure's own implementation - including a simple utility, and a test.

(defmacro defn+
    "same as Clojure's defn, yielding public def"
    [name & decls]
    (list* `defn (with-meta name (assoc (meta name) :public true)) decls))

(defmacro defn
    "same as Clojure's defn-, yielding non-public def"
    [name & decls]
    (list* `defn (with-meta name (assoc (meta name) :private true)) decls))

(defn mac1
    "same as `macroexpand-1`"
    [form]
    (. clojure.lang.Compiler (macroexpand1 form)))

(let [  ex1 (mac1 '(defn f1 [] (println "Hello me.")))
        ex2 (mac1 '(defn+ f2 [] (println "Hello World!"))) ]
    (defn f1 [] (println "Hello me."))
    (defn+ f2 [] (println "Hello World!"))
    (prn ex1) (prn (meta #'f1)) (f1)
    (prn ex2) (prn (meta #'f2)) (f2) )
5
votes

If the "helper functions" are very likely to be used once, you could choose to make them locals of your bigger functions, or write them as anonymous functions. See letfn: http://clojuredocs.org/clojure_core/clojure.core/letfn and http://clojuredocs.org/clojure_core/clojure.core/fn.

I hardly ever use letfn myself.

5
votes

As stated by @kotarak, there is no way (as far as I know) to do that, nor is it desirable.

Here is why I dislike defn- :

I found out that when using various Clojure libraries I sometimes need to slightly modify one function to better suit my particular needs. It is often something quite small, and that makes sense only in my particular case. Often this is just a char or two.

But when this function reuses internal private functions, it makes it harder to modify. I have to copy-paste all those private functions.

I understand that this is a way for the programmer to say that "this might change without notice".

Regardless, I would like the opposite convention :

  • always use defn, which makes everything public
  • use defn+ (that doesn't exist yet) to specify to the programmer which functions are part of the public API that he is supposed to use. defn+ should be no different from defnotherwise.

Also please note that it is possible to access private functions anyway :

;; in namespace user
user> (defn- secret []
        "TOP SECRET")

;; from another namespace
(#'user/secret) ;;=> "TOP SECRET"