Full Disclosure: Privacy in Clojure
Private methods are an interesting thing to observe in Clojure.
The rationale behind setting a method as private is to ensure that the user interacts with a public interface, and all of the public methods associated with it. At a basic level, private methods are accessed within a class (or, with Clojure, namespace) whereas public methods can be accessed and called outside of that class or namespace. And private methods house implementation details, which public methods rely on, but do not need to be disclosed. For instance, if I wrote an algorithm to determine which shoes to wear on any given day based on the weather, you wouldn’t necessarily want the inner workings of that method to be exposed. Providing access to it would make it susceptible to unwanted changes. You, the user, could alter the method and make me wear rain boots on a sunny day.
In this sense, private methods are prone to changes in the future and therefore deemed “unreliable.” By interacting with only public methods through an interface, you’re future proofing your code. You’re relying on more abstractions, not concretions; you’re relying on things that are more unlikely to change. (Remember our lessons learned from good ol’ POODR days?)
While you should implement every method by first writing a test, private tests can be eliminated after the method has been privatized. Private methods should not have a test. It’s a simple rule. Since private methods are called by public methods, the test for the latter should cover the former.
But the truth is, private methods aren’t really private in Clojure. Yup, it’s true. You can signify a private function by defining that function with `defn-` but it is more of a visual indicator and there is no built-in notion of private functions For instance, you can signal a private function like so:
“Birkenstocks are the way to go!”)
I also learned that you can signal a private method by attaching a metatdata tag. Metadata is signaled by this up-symbol (^) and is a map of values that can be attached to various forms in Clojure. Again, this is a visual indicator. Metadata is used typically for documentation, compilation warnings, type hints (used to tell the compiler what type the value will be, thereby making it faster), and other features. See below for an example:
(defn ^:private choose-my-shoes
“Wear yer Yeezies.")
If both of these functions existed in the default `user` namespace, you can call them within `user`. But, if you attempt to call that function in another namespace — just enter `(ns literally-anything-else)` into the repl and try to call that method — you’ll get an error message that looks like this:
var: #'user/outfit-generator is not public, compiling:(/private/var/folders/dy/x769x24n53xbsxphz77svwdc0000gn/T/form-init4161464086709702979.clj:1:1)
You can still print “Birkenstocks are the way to go!” or “Wear yer Yeezies.” by using this syntax:
While I’ve learned that this can be nifty for testing your code, Clojure for the Brave and True tells us that we should rarely do it. And since that book is like my Holy Bible of Clojure, I’m going to stay put and do as I’m told.