Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

style guide: switch to snake case for functions #1097

Closed
isaachier opened this issue Jun 10, 2018 · 78 comments
Closed

style guide: switch to snake case for functions #1097

isaachier opened this issue Jun 10, 2018 · 78 comments
Labels
breaking Implementing this issue could cause existing code to no longer compile or have different behavior. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@isaachier
Copy link
Contributor

I understand the general rule does not use snake_case for function. Why do eql_slice_u8 and hash_slice_u8 break this rule?

@tgschultz
Copy link
Contributor

tgschultz commented Jun 10, 2018

There are actually quite a few places where the std lib breaks formatting guidelines or is generally inconsistent. For example, many fields in the structs produced by @typeInfo are snake_case despite being of type type, std.mem has readIntBE and readInt while std.io has readIntBe and readIntVar, etc.

I think it is just a case of being pretty low on everyone's priority list until zig nears 1.0.0.

@andrewrk andrewrk added this to the 1.0.0 milestone Jun 10, 2018
@bheads
Copy link

bheads commented Jun 11, 2018

I would also want to standardize names (printAlloc vs allocPrint, isXXXX setXXXX getXXXX findXXXX) and common param orders (ie is alloc always the first param except when passing self). Also the stdlib should set a good example and use enums judiciously even for simple flags.

@andrewrk andrewrk added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Apr 15, 2020
@andrewrk andrewrk changed the title Snake case for functions style guide: switch to snake case for functions Apr 15, 2020
@andrewrk andrewrk modified the milestones: 1.0.0, 0.7.0 Apr 15, 2020
@andrewrk andrewrk added the breaking Implementing this issue could cause existing code to no longer compile or have different behavior. label Apr 15, 2020
@andrewrk
Copy link
Member

I am amending this proposal with how to handle initialisms and acronyms in TitleCase things. In summary: Don't mix-mangle the case of initialisms or acronyms.

Before:

fn readU32Be() u32 {}
openZ() open0()
Os
Json
Url
XmlHttpRequest
ServeHttp
ServeHttpOs

After:

fn read_u32_be() u32 {}
open_z() open0()
OS
JSON
URL
XML_HTTP_Request (use underscore even in TitleCase to indicate border between initialisms/acronyms)
XML_HTTP_Request2
XML_HTTP_RequestRead_JSON (no underscore between Request and Read)
XML_HTTP_RequestRead_JSON_u32 (don't mangle primitive type names for the sake of TitleCase)
XML_HTTP_Request_u32 (need underscore to show where Request ends and u32 begins)
XML_HTTP_Request_u32_2
ServeHTTP
ServeHTTP_OS

If those things were functions:

os()
json()
url()
xml_http_request()
xml_http_request2()
xml_http_request_read_json()
xml_http_request_read_json_u32()
xml_http_request_u32()
xml_http_request_u32_2()
serve_http()
serve_http_os()

Reasoning:

Our previous camelCase style has flaws:

  • The information it attempts to communicate does not work for single-word function names.
  • It requires case-mangling acronyms/initialisms.

This is unfortunately a widely breaking change and feels annoying to update code just for an arbitrary style decision, so my suggestion for how to roll this out, if accepted, would be to wait until we have the ability to do identifier-renaming (the feature that some IDEs provide) and it works well. That feature would ease the burden of transitioning a codebase from one style to the other, and we could even provide a script for people to run which would make the changes automatically (the script would have listed all the std lib identifiers that changed; it would not arbitrarily change users' own identifiers).

@jorangreef
Copy link
Sponsor Contributor

I came here to issues hoping to find there was a plan to fix the style guide.

Awesome to see this will be addressed.

@artob
Copy link

artob commented Jul 23, 2020

The ayes would seem to have it (good, IMHO).

@andrewrk For those of us busy building Zig libraries, it would be rather helpful to know soonest whether this proposal--per the examples you gave above--will be accepted or not, so as to not have to frustrate downstream users with massive renames later on.

@andrewrk
Copy link
Member

Note to future self in about 2 weeks: don't postpone this one to 0.8.0 make a decision!!

@Rocknest
Copy link
Contributor

Right now the style guide helps to see when something is a function or a variable or a namespace, with this only type would be easily recognisable. In IDE you would see the type kind (and much more) on hover, however it is nice to have that communicated in plaintext.

@andrewrk
Copy link
Member

That's true, and was the main motivation for status quo style, but it doesn't work for functions with only 1 word in them, so there was always that cognitive dissonance.

BTW did you know that ZLS+vscode has semantic highlighting? It makes functions a different color, so you don't even have to hover.

@Rocknest
Copy link
Contributor

Rocknest commented Sep 18, 2020

I haven't tried ZLS yet.

Before i mostly used Java which has similar preferred style to Zig and i not even once had a problem naming a function that has an acronym or an abbreviation (for me readU32BE is okay, however more idiomatic name in zig would be readU32(.Big, ...); as for single word functions that is not a big problem, but it certainly does not worth extending to all functions. When making single word variables of type function i often add a suffix readFn). Also it seems that Java's preferred style is no longer all caps acronyms, for example few recently added classes java.time.chrono.IsoChronology and java.net.http.HttpClient

@tauoverpi
Copy link
Sponsor Contributor

While semantic syntax highlighting is nice it requires editor support and the experience for non LSP users is degraded. Basic completion methods work rather well the minute the second word is hit which would be made less effective by snake_case matching functions, variables, and so on. The shape change is, in my opinion, much easier to follow as TitleCaseTypes clearly separate from regularFunctions and variables_with_snake_case.

On the topic of semantic highlighting, switching highlighting based on what you want to focus on as in https://buttondown.email/hillelwayne/archive/syntax-highlighting-is-a-waste-of-an-information/ would (arguably) be better with a shape difference since the highlight focus changes based on the task and removing the difference between functions and other things for the other modes.

@jdknezek
Copy link
Sponsor Contributor

jdknezek commented Oct 6, 2020

FWIW I'm a fan of camelCase for function/value distinction, and a fan of case mangling to avoid awkwardness of things like XMLHTTPRFCReader.

@SpexGuy
Copy link
Contributor

SpexGuy commented Nov 5, 2020

Talked with @andrewrk and @thejoshwolfe. We decided to keep approximately the current naming conventions, but clear up any ambiguous cases with the following specific algorithm:

To produce a name, first write out the name all lowercase with spaces. Then, consider the type of variable:

  • instantiated (non-namespace) type => capitalize each word and delete the spaces.
  • function => capitalize each word except the first and delete the spaces.
  • anything else => replace the spaces with underscores.

If at any time you would delete a space and the character to the right of the space is a number, instead replace the space with an underscore. anytype values fall into the "anything else" category.

Some examples:
xml http request => XmlHttpRequest
read u32 be => readU32Be
read u 32 id => readU_32Id
open u32 2 => openU32_2
os handle => OsHandle
a b test => ABTest
ab test => AbTest
value as u32 => value_as_u32
my u32 module => my_u32_module

This transformation is both unambiguous and invertible.

@SpexGuy SpexGuy closed this as completed Nov 5, 2020
@daurnimator
Copy link
Collaborator

@SpexGuy any recommendations for if the name starts with a number?

@SpexGuy
Copy link
Contributor

SpexGuy commented Nov 5, 2020

Uh, taking off my "official" hat for a second, but maybe don't? I don't think it's unreasonable to disallow identifiers which start with numbers in the standard library.

@daurnimator
Copy link
Collaborator

@SpexGuy one such example might be crypto functions; at the moment we are going to have:

pub const aead = struct {
    pub const aegis = struct {
        pub const Aegis128L = @import("crypto/aegis.zig").Aegis128L;
        pub const Aegis256 = @import("crypto/aegis.zig").Aegis256;
    };
    .....
};

It would make a lot of sense to have aead.aegis.128L instead of aead.aegis.Aegis128L, where we only really keep the Aegis prefix so that the name doesn't start with a number.

@jedisct1
Copy link
Contributor

jedisct1 commented Nov 5, 2020

AEGIS128l is the name of the algorithm, not "128l". Splitting that name wouldn't make sense, and would break grepability as well.

@SpexGuy
Copy link
Contributor

SpexGuy commented Nov 5, 2020

Oh, yeah, if you would capitalize a word and it starts with a number, leave it as is, and the space to the left of it will always become an underscore because it's a number to the right of a space. So under these rules it would be
aegis 128 l => Aegis_128L. That's a little weird but unambiguous.

@MasterQ32
Copy link
Contributor

@SpexGuy @andrewrk should we link the answer above in the FAQ, so the current style guide is easily discoverable?

@ifreund
Copy link
Member

ifreund commented Nov 5, 2020

any recommendations for if the name starts with a number?

Identifiers can't start with numbers unless you use @"123" syntax:

IDENTIFIER
    <- !keyword [A-Za-z_] [A-Za-z0-9_]* skip
     / "@\"" string_char* "\""                            skip

@kuon
Copy link
Sponsor Contributor

kuon commented Jun 22, 2022

I realize this proposal has been closed, but I started zig a while ago and I find the camelCase for function very disturbing.

I don't want to start a debate, but I'll just share my experience as a developer.

In short, I can read TitleCase and snake_case fine, but I have a lot of trouble with camelCase. I've been coding for 30 years, and its always been a pain point. In my head when I see a camelCase word there is a long pause, like it was written camel. Case. Which makes the code much harder for me to read.

Apparently, I am not alone, I found a study about this https://ieeexplore.ieee.org/document/5521745 but I do not know how significant this is.

Again, I do not want to start a debate, nor even think of changing anything in zig. But I do use snake_case for functions in all my code (and TitleCase for types). And I hope the code I write (libraries (I have a lot of projects)..) will be accepted by the zig community without being frowned upon as "bad style".

@andrewrk andrewrk modified the milestones: 0.13.0, 0.12.0 Jul 9, 2023
@andrebrait
Copy link

Whatever is the end result, what I can't deal with is the mixing of the two concepts.

It's too hard on the eyes to beReadingThis and_suddenly_this and it's specially weird_when_they_are.combinedTogetherAndWhatnot.

Just the code samples have enough examples of cases where the two are mixed together and it's very visually confusing.

@cdecompilador
Copy link

agree, i support snake_case or go all out with CamelCase since those two are the main two styles followed by many c programmers, mixing 3 styles is bad idea

What i don't agree is with delaying this much more, since the refactor won't be automatic since sometimes what seems good in camelCase doesn't with snake_case, like for example getters, which usually are name instead of getName

@RaphGL
Copy link

RaphGL commented Sep 23, 2023

If you allow me to bikeshed a bit here, here's my take on it :)

One of Zig's primary goals is to interop with C and be another system's programmming language. As such I think adopting conventions that are compatible with those would be beneficial since snake_case is the de facto standard for C, C++, Rust and their standard libraries (even though some C/C++ libraries have opted to use another style).

I personally would propose the following style:

  • Every type and every function that returns a type is PascalCase
  • Constants can use SCREAMING_SNAKE_CASE if they're just a value known at comptime
  • Enums and Errors also use PascalCase as they're not really plain constants anymore like in C and you can't just access their value without @intFromEnum
  • Everything else is snake_case
// Functions
fn LinkedList(comptime T: type) type;
fn remove_duplicates() T;

// Variables
const MAGIC_NUMBER = 0x4e93fd;
const immutable_value = get_value();
var mutable_value = get_value();

// Enums and Unions
const MyEnum = enum {
    OptionOne,
    OptionTwo,
};

// becomes pascal case cause you have to unwrap it like an enum
const TaggedUnion = union(enum) {
    FirstValue: i32,
    SecondValue: f32,
};


// just plain old unions so no PascalCase
const RegularUnion = union {
    first_value: i32,
    second_value: f32,
};

@andrebrait
Copy link

andrebrait commented Sep 23, 2023

// Enums and Unions
const MyEnum = enum {
    OptionOne,
    OptionTwo,
};

// becomes pascal case cause you have to unwrap it like an enum
const TaggedUnion = union(enum) {
    FirstValue: i32,
    SecondValue: f32,
};

Enums are constants. I'd rather see them use SCREAMING_SNAKE_CASE instead.

@andrebrait
Copy link

andrebrait commented Sep 23, 2023

@RaphGL also, constants should use SCREAMING_SNAKE_CASE (or snake_case, whatever, a single style is what I mean) no matter whether they're known at compile time or not.

Outside code shouldn't know what your implementation is like and shouldn't have to change if you change your implementation either. That's also likely a good thing for your own internal code too.

I'd hate to review an MR with 200 changed lines where the only actual change was making COOL_CONSTANT into cool_constant because it was a literal but now it's taken from a function or something. And that's just for stuff you don't export. For stuff you do, that'd be an instant compile error for anything that uses your library and, except for specific cases, external code really shouldn't care where you took the value from.

A constant is a constant.

@andrebrait
Copy link

The problem with having different styles for different classifications of things is that it becomes very unstable under refactoring and whatnot too.

Made a variable inside a Union constant? Change its casing, now change every place that uses it, etc. now remember you broke every external thing that touches that variable (now a constant) because of that.

Or don't, and now you have an inconsistent style and it doesn't make things obvious anymore.

@RaphGL
Copy link

RaphGL commented Sep 24, 2023

@RaphGL also, constants should use SCREAMING_SNAKE_CASE (or snake_case, whatever, a single style is what I mean) no matter whether they're known at compile time or not.

Yeah but zig makes no distinction between constant and immutable value and most of the learning resources recommend making things immutable whenever possible so using SCREAMING_SNAKE_CASE would litter your code with capitalized variables.

I'd hate to review an MR with 200 changed lines where the only actual change was making COOL_CONSTANT into cool_constant because it was a literal but now it's taken from a function or something.

Yeah that might be a possibility if it were a comptime function. This is also only an annoyance because ZLS does not support bulk renaming yet and for people that don't use an LSP (in the future).

And that's just for stuff you don't export. For stuff you do, that'd be an instant compile error for anything that uses your library and, except for specific cases, external code really shouldn't care where you took the value from.

If you're exporting it, people don't care how that constant is built just that it's one, so making it screaming case is ok.


I only made this distinction for constants because seeing screaming case everywhere would be too noisy. I'd be fine with that distinction personally but your criticism is very valid, I'm not sure how often this would be an issue if you just make all user facing constants SCREAMING_CASE. Feel free to point out more flaws in my suggestion :)

@N00byEdge
Copy link
Sponsor Contributor

I think the screaming vs snake case thing is getting a little bit off topic (but I'll pitch in that I prefer snake case for all the constants, enum variants etc). This issue is about function names.

@kuon
Copy link
Sponsor Contributor

kuon commented Sep 25, 2023

It seems there some consensus on renaming functions to snake_case. For personal preferences, similarity to rust and others (elixir, ruby, many C libs...), and the "pain point" of mixing styles. I "think" we can consider this decided.

For constants, I think further discussions is necessary, especially when we consider zig notion of constant to be different than it is in other languages because of comptime, but I suggest we start a new issue, as this one is rather long and "should" be focused on function names.

@cdecompilador
Copy link

cdecompilador commented Nov 8, 2023

Ok, to sum up, what could be said that most of us agree to

  • functions snake_case
  • types/type generators PascalCase, mayus acronyms and allow Pascal_Snake_Case for some cases like acronym + name (ex: XML_Request) ✅
  • constants style require a new issue 🆕
  • I've seen some mention of enum variants being PascalCase but that should discussed a new issue 🆕

Let's close this and start discussion over how to do it, which in my opinion there is no painless way

@sanks02
Copy link

sanks02 commented Nov 8, 2023

@cdecompilador I don't think this issue needs any further summaries, @kuon did it well. Types in particular are out of scope, and I don't think there is agreement on XML_Request, so I think everyone (including me) should strongly consider creating a new issue if we want to move a proposal with a style for types forward.

@lucianboboc

This comment was marked as spam.

@softprops
Copy link

Has anyone guesstimated the breaking change impact on std lib if it itself were to reflect the proposed changes?

I think it’s standard practice to seek inspiration from std libraries when trying to understand what good looks like.

left unchanged it would be weird to have a recommendation that the std lib itself doesn’t follow.

@silversquirl
Copy link
Contributor

Guesstimated std breaking changes: all of it :)

@mlugg
Copy link
Member

mlugg commented Mar 11, 2024

@andrewrk

Anyone have any ideas how to roll out a change like this with minimal pain and suffering?

I just threw together a little script which automatically converts all camelCase (but not PascalCase) identifiers in a Zig file to snake_case, aside from any which is an export/extern. It'll query the user for any where the conversion isn't immediately obvious (e.g. getPath2 -> get_path_2 but isUtf8 -> is_utf8). We'd have to be careful applying it over something as big as the standard library or compiler, but I'd think it should work well in most codebases.

@softprops
Copy link

It would be a nice feature of zig fmt to aide in such a code reformatting formatting. The rust community tooling also has a fix command for these kinds of changes. A zig fix does not yet exist but would also be nice.

@WhiteHexagon
Copy link

I think that after 5 years this issue should be closed. It is hard enough already getting Zig code samples out of the various LLMs / GPTs, and such a fundamental change this late in the project is going to set us back a long way.
Not to mention all the tutorials, examples and SO QA type sites that wont get updated, and for what benefit? to try to copy rust?
If we are trying to create a better 'C', shouldnt that mean even more stability than what 'C' had? Isn't that why 'C' has endured all this time, and is still a 'goto' choice for many projects. Imagine picking up a KnR C style book for Zig that would still be valid 10 years from now... yeah, I know, old man dreaming :)

@mlugg
Copy link
Member

mlugg commented Mar 11, 2024

Optimizing Zig to be a language LLMs can write effectively is quite possibly our single lowest priority. Zig is a language written for humans; not for glorified autocompletion engines. To be frank, if one is seriously trying to get ChatGPT or the like to output good Zig code, naming conventions will be the least of their concerns.

I'm not sure what comment gave you the impression that this proposal exists "to try to copy Rust". The proposal exists because a lot of people believe this change would improve readability of Zig code and/or simplify the style guide.

Being in v0, Zig is currently an unstable language. Once it reaches v1, the intention is that it becomes highly stable, both technically and in terms of conventions; but for now, major changes to the language, style guide, conventions, etc are all perfectly allowed, and even encouraged if they are clear improvements.

@andrewrk andrewrk modified the milestones: 0.13.0, 0.12.0 Mar 11, 2024
@andrewrk
Copy link
Member

Closing again. I toyed with this in https://github.com/andrewrk/autodoc and found it to tend to cause unfortunate shadowing that was not really the case before. camelCase for function names I think is serving us fairly well.

That said, it's just what we're going to do in the standard library - individual projects can obviously do their own naming conventions. And sometimes projects doing their own naming conventions can be handy in its own way, the human understands that one area of code has this vibe, another has that one.

I'll also consider changing the naming conventions of builtin functions. For whatever reason, camelCase in builtin functions just feels wrong. I often mistype @typeInfo and @TypeOf in particular.

@andrewrk andrewrk closed this as not planned Won't fix, can't repro, duplicate, stale Mar 11, 2024
@McSinyx
Copy link
Contributor

McSinyx commented Mar 12, 2024

That said, it's just what we're going to do in the standard library - individual projects can obviously do their own naming conventions.

I suppose with comptime it's possible to convert function and constant names (but not globals) from one convention to another when @import. Is it possible to this without messing with lazy compilation?

@kuon
Copy link
Sponsor Contributor

kuon commented Mar 13, 2024

While I understand @andrewrk choice, I think that if there are naming conflict in the std lib that are prevented only by different case it can be a little bit confusing and feels "brittle", and maybe it should be addressed in another issue.

That said, it's just what we're going to do in the standard library - individual projects can obviously do their own naming conventions.

At any rate, it is the only important thing to me. I use snake case in JS and I have no problem with std lib and DOM lib being camelCase. As long as zig makes no technical decision on the name, it is OK.

I know I did push a bit hard for this to be implemented, and I am sorry if it was too much. In the end it is only a personal preference. Cheers.

@pycaw
Copy link

pycaw commented Mar 13, 2024

While I understand @andrewrk choice, I think that if there are naming conflict in the std lib that are prevented only by different case it can be a little bit confusing and feels "brittle", and maybe it should be addressed in another issue.

That said, it's just what we're going to do in the standard library - individual projects can obviously do their own naming conventions.

At any rate, it is the only important thing to me. I use snake case in JS and I have no problem with std lib and DOM lib being camelCase. As long as zig makes no technical decision on the name, it is OK.

I know I did push a bit hard for this to be implemented, and I am sorry if it was too much. In the end it is only a personal preference. Cheers.

It is a preference but beyond personal as the comments and reactions in this thread show. I fail to see the current resolution being consistent with it all or your summary/meta-analysis.

(My personal opinion is that there should be no camelCase at all; and no SCREAMING_SNAKE_CASE either on a side note).

wooster0 added a commit to wooster0/zig that referenced this issue Mar 14, 2024
ziglang#1097 is closed.

> it's just what we're going to do in the standard library

If it's what we're going to do in the standard library these two
functions should probably follow suit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking Implementing this issue could cause existing code to no longer compile or have different behavior. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests