clojure-style-guide icon indicating copy to clipboard operation
clojure-style-guide copied to clipboard

defn- vs. defn ^:private

Open looselytyped opened this issue 9 years ago • 19 comments

Looking here it is suggested that

;; good
(defn- private-fun [] ...)

(defn ^:private private-fun [] ...) ; overly verbose

However reading up on this Google group discussion it seems that using defn- is a bit contradictory since there are other def that do not have a complimentary def- option. Quoting Vincent

It's better to use ^:private metadata on the symbol naming the var, since there is also defmacro, defmulti, etc, which not have hyphened versions too.

Should this continue to remain in the styleguide?

(I can submit a PR to make the change, however I felt that perhaps having a discussion prior to that would be appropriate)

looselytyped avatar Nov 24 '15 14:11 looselytyped

That's one interesting point. However, I don't recall ever seeing private macros or multimethods. Maybe we should just extend this a bit, to mention this consideration.

bbatsov avatar Jan 21 '16 05:01 bbatsov

My team prefers (defn ^:private ...) because it's consistent with the way to def a private var.

eigenhombre avatar Jan 22 '16 00:01 eigenhombre

We prefer defn- here and lament the lack of def-. I personally consider ^:private to be ugly.

philjackson avatar Jan 25 '16 13:01 philjackson

def- and soforth don't exist for good reason, you get a combinatorial explosion for every variation. Having def- begs the question of why we don't have defmulti-, defprotocol- and on down the line. How about definline-. Maybe you want a postfix so you can elide ^:dynamic. Lets say -d. Now defd-? ^:static with -s, defs, so you also need defs-? This gets really silly really quickly and doesn't exist for this reason. I suspect were someone to do-over Clojure defn- probably wouldn't make the cut. Because the -- convention isn't a general thing, ^:private should be preferred since it does work everywhere and is explicit in its meaning.

Edit: said prefix, meant postfix.

arrdem avatar Jan 25 '16 19:01 arrdem

And then there's some that would argue that since :private doesn't really work the way you'd think (private vars can still be accessed via #'), and it inhibits testing, that we should prefer public vars over private vars.

halgari avatar Jan 25 '16 19:01 halgari

There are some projects where people attempted to java-style provide a public API hiding implementation details and helper functions... my experience with ^:private is that I've just used #' to get around it when I found it convenient (meaning ^:private was useless), and that when I used it myself ^:private vars just got moved into util namespaces and ^:private got dropped. So I'm in the "just don't bother" camp mentioned by tb.

arrdem avatar Jan 25 '16 19:01 arrdem

@halgari I think ^:private is a useful way to indicate author intention as to which parts of the ns are considered subject to change and which aren't. While it doesn't mean the same thing as it does in other languages, it's still useful.

I personally use and prefer defn- over defn with ^:private b/c it's shorter and again, concisely communicates intent.

@bbatsov I have seen private macros and multimethods (there are certainly some in Clojure itself).

puredanger avatar Jan 25 '16 20:01 puredanger

@arrdem defn is the most common of the symbols you list, having a short-cut for the privacy declaration doesn't seem harmful in any way. Consistency is often great, but in this case, I prefer the conciseness.

philjackson avatar Jan 25 '16 20:01 philjackson

I understand @arrdem's point, but it still makes sense to have shorthands for commonly used stuff (or there should be no shorthands at all for the sake of consistency). I think that def- and defmacro- would probably cover the majority of usages. Dynamic vars are pretty uncommon, so drawing a comparison to them is probably not a good idea. :-)

bbatsov avatar Jan 26 '16 08:01 bbatsov

My 2 cc: the ^:private form also helps with learning. Explaining that (fn []) and #() are quite the same is one thing, but the lack of (def-) & friends normally causes some confusion when you are new to the language.

andrewhr avatar Jan 26 '16 13:01 andrewhr

We will not be adding any other "-" forms to core. I think perhaps if we could do it over, defn- would not be added either, but we're not going to take it away.

puredanger avatar Jan 26 '16 15:01 puredanger

@puredanger What about simply deprecating it (defn-) and keep it around forever (Java-style)? Seems like a good middle-ground to me. Sends the message this was probably not a good idea (and people won't fret over the lack of def- and friends), but doesn't affect the users of the defn-.

bbatsov avatar Jan 26 '16 15:01 bbatsov

I don't think we want to deprecate it, we just don't want to take the idea any further.

puredanger avatar Jan 26 '16 15:01 puredanger

I know I'm coming in a bit late here and I hope that's not a problem. A coworker just asked why I used ^:private instead of defn- and my rationale is the following:

a. I'd rather have a descriptive attribute a beginner, or non-clojurist can understand than use a symbol that only someone who knows clojure can fully understand b. I don't really need to limit access to the functions. I just wish to communicate my intent to separate critical API function versus internal helpers c. On a very subjective level I feel defn- always strikes me as, "Maybe it's a typo?" whereas with ^:private it was more than likely not a mistake and I don't question it.

However, I think context is important here. If it was my job to work on Clojure I'd probably prefer defn- as well since everyone there would be expected to know what it means. Same if I was a Clojure based dev studio where we can count on hiring Clojure experts. Since I'm doing general app development with a few languages it's hard to say who we'll get.

jaidetree avatar May 20 '20 04:05 jaidetree

Me too don't understand why defn- is still considered a good style, when Clojure core contributor mentioned very clear 6(!) years ago (https://github.com/bbatsov/clojure-style-guide/issues/124#issuecomment-174631925), that creating defn- was a bad idea and ^:private is perfect for both def and defn cases.

@bbatsov Please fix the style guide accordingly (in 3 places: (defn ^:private fun []...) = good practice (def ^:private private-var ...) = bad practice (overly verbose - no need to add private in the var name) (defn- fun [] ...) = bad practice (or acceptable, but not recommendable)

makarichevss avatar Dec 22 '22 13:12 makarichevss

That’s not what I said above. defn- is part of Clojure core, in common use, and perfectly acceptable.

puredanger avatar Dec 22 '22 13:12 puredanger

I think some of the confusion in the style guide comes from using private-fun and private-var as if the name has some significance and it would be less confusing to use, say, internal-fun and internal-var instead.

That said, I'm not sure that section adds much value anyway. The only case where there's a stylistic point to argue is:

(defn- internal-fun [] ...)
;; vs
(defn ^:private internal-fun [] ...)

I've never seen the latter in code but, based on the comments here, clearly some people use it and like it -- and I can see the arguments in favor of consistency (using ^:private wherever you want to denote a non-public top-level symbol) so the strongest recommendation the guide should offer is perhaps "good" on the first one and "unusual but acceptable" on the second one... which seems like a pointless recommendation?

Perhaps that whole section should just be deleted as adding no value?

seancorfield avatar Dec 22 '22 17:12 seancorfield

That’s not what I said above. defn- is part of Clojure core, in common use, and perfectly acceptable.

Sorry, I think I misread that post.

For me bad style is when you add no meaning by duplication (that's why (def ^:private private-var ...) is bad - var is private, what's the point to add private in the var name? - yes, one can say that this is a good old DRY principle, but it's up to guide's author whether to include it - for me it's good to remind about DRY because Clojure likes conciseness) or when you use an option that is in the language, but it's not recommended for use.

If everyone uses defn-, that's ok, let it be so.

About synonyms to private - I think it's very arguable theme. Some say private of course is internal, but for another ones that should be a different category (like warm and soft). Maybe it's even closer to universal KISS principle. Because when I read "...private internal...", I stop and think "hmm, so somewhere else may be private external things? ...or public internal?" - anyway, the code becomes harder to read and understand.

makarichevss avatar Dec 22 '22 18:12 makarichevss

@makarichevss You are misunderstanding my point: I suggested internal only so there wasn't duplication between private in the Var name and the metadata -- the Var name is unimportant here: it could be:

(defn- wibby-wobbly [] ...)
;; vs
(defn ^:private wibbly-wobbly [] ...)

The point is not the name, it's how you indicate something is private in the Clojure language.

Which is why I said I don't think the Private section is adding any value here, as it currently stands.

seancorfield avatar Dec 22 '22 18:12 seancorfield