I just came across this reply to a new package announcement. In it, they suggested removing underscores from function names to make them more idiomatic for Julia.
The question is, is Julia today still strictly abiding mashedcase naming? The SciML Style Guide suggested using snake_case, showing that the community and the core developers might have different opinions when it comes to function naming. In core Julia, it seems that snake_case is mainly used for internals only, but why be inconsistent?
I really want to know the communityâs opinion on the current state of Julia function naming, in core code vs public packages vs normal code. To me, snake_case could seem too verbose, but the readability benefits might outweigh this. Has there been a general consensus on the topic?
There is nothing wrong with using snake_case naming in the julia package ecosystem. Things like camelCase are seen as unusual though.
Julia Base itself has a self-imposed restriction that they try not to introduce any API names that are snake_case and instead use mashedcase, which is fine, but youâd be in good company if you decide you prefer snake_case, and should just ignore people complaining about it. Itâs perfectly idiomatic.
From what Iâve seen, the style guide itself is quite ambiguous, unlike PEP8 which is more strict and definitive. I think itâs true that we donât have the power to enforce anything, but having something more definitive to follow is quite less mental burden and more actual coding. I hate being too obsessed with code style! Maybe Iâd just use snake_case regardless so as to reduce decision fatigue.
I disagree. If Julia Base promotes a certain code style, and the vast majority of packages sticks to it, I donât see a very good argument to develop packages in different styles. At the end of the day, that would imply a spaghetti script with mixed code style from different packages:
using A # mashedcase
using B # snake_case
using C # camelCase
A.myfunc(B.my_const, C.otherFunc(B.other_const))
I think we wonât go as far as using camelCase for function names in Julia, mixing mashedcase and snake_case together could be bad but wonât be unhinged.
I would urge you not to use mashedcase. It is probably the worst style decisions that ever made it into Julia. The problem with mashedcase is that is does not scale: at some point, mashedcase becomes completely unreadable. Where exactly it crosses the boundary from âreadableâ to âI have to include some underscoresâ is pretty subjective. The result is that the names of functions become unpredictable.
My recommendation is to leave mashedcase for Julia core, and use snake_case consistently for functions throughout the ecosystem, and CamelCase for types and modules.
Even Julia Base had public API absurdly long like require_one_based_indexing though, which uses snake_case instead of mashedcase (it was an internal until it wasnât, but the snake_case name is kept), the inconsistency is wild.
I have a hard time getting into the mindset of finding that to be a âwildâ inconsistency. At least to my eye, mashedcase and snake_case sit together side-by-side just fine, and which one I use really just depends on how clear or unclear the mashedcase version looks.
Might be a quirk of my psychology, or a result of being immersed in juliaâs ecosystem where the two are often freely mixed. But I really just donât get why people get worked up over this stuff unless it becomes an actual impediment to reading (which mashedcase does become past a certain length).
I follow this for all new functions, and only if I am extending existing functions (for me also just form Base ones) I of course continue their mashed case, e.g. for isapprox. In general I prefer snake for readability. Sure if mashedcaseinlongername is still unambiguous and readable, other package developers might also stick to that, but I do not like mashed. I think to some extend they can well live side-dy-side.
I personally prefer snake_case, but I think that choosing the actual name well (and in general, good API design) is much more important than this issue.
I fail to see why that is such a big deal. If someone actually likes camelCase, but they write a great package, with a well-designed API to boot, I will just happily use it. As for mashedcase, I would not bat an eyelid.
I have to agree, I believe Iâm obsessing over this too much. But since Iâm coming from languages that largely had consensus on naming styles, coming to Julia a few weeks ago, this definitely made me a little uncomfortable. Iâm exaggerating the âwildâ inconsistency though, sorry for that.
As a potentially helpful historical note, (that I might be misremembering), one reason that Base sets the mashedcase rule for itself was that they felt that for a Base language API, it would be a code-smell to have overly long, complicated, descriptive names.
A base API should be a common base of shared idiots that forms the language, and so they should be commonly recognized idioms, and relatively simple concepts. So using mashedcase forced the language API to choose shorter simpler names.
Packages are not the same. They are often for niche concepts, and are not providing concepts that everyone should be expected to know ahead of time in order to use the language. Therefore there are many cases where longer_more_descriptive_names are appropriate, and for that, snake_case just is a better fit.
Yeah, I had a time where I did not like that one of my packages had ( and still has) isapprox and is_point â but by now I made my peace with that, exactly because I am fine with Base names being mashed and âmineâ being snake.
Beyond camelCase and snake_case, still on naming conventions, I find the use of a bang ! at the end of mutating function names quite odd.
Itâs not familiar to people coming from other languages (specially because ! is often not even allowed in names)
It does not specify what is mutating
it looks awkward when combined with the negation operator.
Sometimes it makes difficult to find function names depending on the tools, e.g. regex
Personally, I prefer more explicit names using add_, set_, _inplace, etc., (without the bang), and I also make the mutating behavior clear in the documentation.
In this context, many Julia functions would not even need a bang, e.g. push!, pop!, append!, delete!, setindex!, insert!, etc., where the bang arguably worsens readability.
Other functions like sort!, map! would be sort_inplace, map_inplace or apply_map etc., which is very idiomatic.
Thatâs the Julia idiom I like the best! A name ending with _inplace also doesnât specify what is mutating, and the existence of a ! certainly shouldnât stand alone without documentation of what is happening.
Itâs not something semantically meaningful to the language â itâs just a hint to humans that thereâs some mutation happening. And Iâm almost always grateful to get that hint.
âŚunless youâre familiar with Lisp, Scheme, or Ruby, which use the same convention.
Youâre right that you need to check the docs to be sure what is mutated, though itâs almost always the first argument. But knowing that a function call has side effects makes it much easier to reason about what code is doing. And very often functions have mutating and non-mutating variants, so itâs good to have a simple convention to distinguish them.
I usually avoid underscores in function names by designing APIs that are made of simple verbs and structs that hold complex state (and name).
Instead of writing required_action_to_take(args...) like in C, I take this long name as a sign that the software design can be improved. I introduce auxiliary structs to hold state ComplexMethodInCamelCase and a simple short verb (e.g., perform) to execute:
Iâve been doing this across all the packages I maintain, and never encountered a single instance where snake_case was necessary to improve clarity. I feel that snake_case is basically a consequence of using Julia with little to no abstraction.