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

Change style guide for "namespaces" to be TitleCase #1884

Closed
Hejsil opened this issue Jan 18, 2019 · 15 comments
Closed

Change style guide for "namespaces" to be TitleCase #1884

Hejsil opened this issue Jan 18, 2019 · 15 comments
Labels
proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@Hejsil
Copy link
Contributor

Hejsil commented Jan 18, 2019

Relies on #1047 being implemented:

  • It's a simpler rule.
    • My proposal:
      • "If @typeOf(x) == type or x is a function returning type then x should be TitleCase"
    • Current (after namespace changes):
      • "If @typeOf(x) == type and std.meta.fields(x).len != 0 or x is a function returning y where @typeOf(y) == type and std.meta.fields(y).len != 0, then x should be TitleCase"
  • It makes it clear when we are calling a "method" over a function (related: support clearer "method" syntax on structs? #746)
    • Function will always be namespaced with nothing or a TitleCase name: Namespace.func()
    • Methods will always be namespaced with a snake_case name: instance.method()
    • This makes it easier to argue about the state in your program.
      • Methods probably modify the state of their instance (we don't know at call site because we don't know if instance is passed by copy, *const or *).
      • Functions are more likely to modify globals. When parameters are modified, the caller can see it in their code Thing.func(&instance).
  • Free's up useful names for variables and parameters.
    • Many good snake_case and camelCase names are taken up by std, keywords and my own namespaces (the following list is words I've wanted to use for variables and functions before, but couldn't without then prefixing my namespace).
      • std: mem, json, mutex, event, drawf
      • keywords: section, align, type, and, or, export, error, catch
      • My own libs: slice, platform, scan, bits, parser, string, compare
    • Short, descriptive names are more valuable than long ones, so the more we free up for commonly used things (functions and variables) the easier our code will be to read.
    • This will also even out the distribution of names more evenly. Currently, TitleCase names are rarely declared, and single word snake_case and camelCase names are extremely common.
@andrewrk andrewrk added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Jan 18, 2019
@andrewrk andrewrk added this to the 0.5.0 milestone Jan 18, 2019
@andrewrk
Copy link
Member

Just to get a little preview of what this would look like:

const Std = @import("Std");

pub fn main() !void {
    // If this program is run without stdout attached, exit with an error.
    const stdout_file = try Std.Io.getStdOut();
    // If this program encounters pipe failure when printing to stdout, exit
    // with an error.
    try stdout_file.write("Hello, world!\n");
}

@justinbalexander
Copy link
Contributor

I do think that looks nice.

@jayschwa
Copy link
Contributor

jayschwa commented Jan 19, 2019

Std.Io.getStdOut()

I propose that acronyms are capitalized in the traditional way when used in a TitleCase context. Ex: IO instead of Io, HTTP instead of Http, etc. So, Std.IO.getStdOut() in the above example. A hypothetical long form would be InputOutput. A hypothetical shortened form would be InOut. Following that, I think IO makes sense.

@matthew-mcallister
Copy link
Contributor

@jayschwa I broadly advise against that convention. You wind up with names like XMLHTTPRequest and JSONRPC where you can't identify initialism boundaries without prior knowledge. This can be a nuisance for code generation; e.g. converting a type name to a function name might result in make_xmlhttp_request instead of make_xml_http_request. For consistency, I suggest avoiding this style anywhere in code, including in namespaces.

@mikdusan
Copy link
Member

mikdusan commented Mar 5, 2019

tl;dr; UpperCamelCase frustrates when using abbreviations or acronyms, which as it so happens, is the most popular for namespace-names.

my 2 cents as a zig newcomer: NO to UpperCamelCase namespaces:

Premise assumptions and broad strokes about a namespace-name:

  1. current decision is changing from snake_case → UpperCamelCase
  2. prefer shorter and more convenient as compared to type-name preferences
  3. unlikely to be multi-worded
  4. likely to be one of { word, acronym, abbreviation }
  5. there is no (yet) built-in Zig versioning for namespaces

Style-1 → current

std.io.debug.warn()
cstr.toSlice()
json.Parser.init()
dynamic_library.LinuxDynLib.open()
DESCRIPTION
appealing abbreviation identifiers; eg. std
appealing acronym identifiers; eg. io
appealing word identifiers; eg. debug
¾ appealing multi-word identifiers; eg. dynamic_library
¼ appealing when using numeric suffix; eg. { sse2, ipv6 }
𐄂 infer is(method-call)? vs. is(function-call)? by looking to LHS of dot-operator
𐄂 avoid collision with lowerCamelCase/snake_case (functions, parameters, variables)

Style-2 → UpperCamelCase as proposed

Std.Io.Debug.warn()
Cstr.toSlice()
Json.Parser.init()
DynamicLibrary.LinuxDynLib.open()
DESCRIPTION
𐄂 appealing abbreviation identifiers; eg. Std
𐄂 appealing acronym identifiers; eg. Io
appealing word identifiers; eg. Debug
appealing multi-word identifiers; eg. DynamicLibrary
𐄂 appealing when using numeric suffix; eg. { Sse2, Ipv6 }
infer is(method-call)? vs. is(function-call)? by looking to LHS of dot-operator
avoid collision with lowerCamelCase/snake_case (functions, parameters, variables)
  • Std: in english print, a mixed-case abbreviation is rare compared to all-lower-case.
    Generally, capitalization is used when abbreviating more or less to the rules of an acronym.
    Thus, when abbreviating a single word we end up with a lower-cased set of letters.
    • option: longer to make a word: Standard; I don't support this because prefer shorter and more convenient as compared to type-name preferences
    • option: all-caps to make unicase: STD; I don't support this because it becomes special treatment for abbreviations and confuses readability with multi-word names
  • Io: in english print, a mixed-case acronym is rare compared to all-caps or all-lower-case for very common acronyms.
    Generally, an acronym is derived by taking the first letter (or syllable) or each word in a group.
    Commonly used acronyms can actually become words in of themselves.
    I see an analogue here that common acronyms are a special kind of acronym just as namespaces are a special kind of type and naturally are suited for snake_case.
    • option: longer to make a word: InputOutput; I don't support this because prefer shorter and more convenient as compared to type-name preferences
    • option: all-caps to make unicase: IO; I don't support this because of the commonality of namespaces and readability with multi-word names

Style-3 → UpperCamelCase and all-caps for { acronym }

Std.IO.Debug.warn()
Cstr.toSlice()
JSON.Parser.init()
DynamicLibrary.LinuxDynLib.open()

Style-4 → UpperCamelCase and all-caps { abbreviation/acronym }

STD.IO.Debug.warn()
CSTR.toSlice()
JSON.Parser.init()
DynamicLibrary.LinuxDynLib.open()

@Hejsil
Copy link
Contributor Author

Hejsil commented Mar 5, 2019

@mikdusan Acronyms already lose there casing in type names according to the current styling so unless you propose that this is also changed, then I don't see this as an argument against changing 'namespace' to be TitleCase.

// Taken from the "Style Guide" section in Zig's documentation
// The word XML loses its casing when used in Zig identifiers.
const xml_document =
    \\<?xml version="1.0" encoding="UTF-8"?>
    \\<document>
    \\</document>
;
const XmlParser = struct {
    field: i32,
};

Type names rarely contain abbreviations so I don't know what is preferred here.

I will say, that it is hard to argue for or against "appeal". It is very subjective and all I can really say here is, that I find Std and Io just as appealing as std and io. I'm not writing an English paper, but a program so I don't mind if we break a few 'rules' in the English language to get the practical benefits that I point out in my proposal.

  • Simpler rule
  • Clearer function vs method call site
  • More available names for parameters, functions, and variables (which is the names you'll be writing the most)

@mikdusan
Copy link
Member

mikdusan commented Mar 5, 2019

@Hejsil,

Acronyms already lose there casing in type names

I don't think that's an argument for changing to TitleCase; rather the opposite; because the type-style discourages(?) all-caps of sub-words in TitleCase; eg. XmlParser is evident and XMLParser is not; tech-print (I'll call it that instead of english-print, meaning to include docs/specs in multiple-speaking languages) mostly likes acronyms which are all-caps or all-lower-case. Doing XmlParser is contrary to this tech-acronym-rule (TAR). If this were a namespace with acronym+words then I'm advocating:

  • xml_next_gen > XML_Next_Gen > XMLNextGen > XmlNextGen.

There are many examples of TAR that work well and commonly, as either all-caps or all-lower-case, but (again subjectively) look awful when TitleCase'd.

UP DOWN Title
BBC bbc Bbs
DTS dts Dts
USA usa Usa
OEM oem Oem
ROM rom Rom
RAM ram Ram
DDR ddr Ddr
DDR4 ddr4 Ddr4
SSD ssd Ssd
HDD hdd Hdd
MP4 mp4 Mp4

I think there is a reason why those acronyms are seen in tech-print almost exclusively as TAR... if I may be so bold to suggest it's because the public at large, including programmers, find it easier. For whatever reason, it is easier and that has value.

I'm not writing an English paper, but a program so I don't mind if we break a few 'rules' in the English language to get the practical benefits that I point out in my proposal.

My argument is not really about sticking to english rules. Rather it's that we understand it is a basis, and has influence over the readability of source code. It is a plus, I think, when we can keep some consistency to TAR.

  • Simpler rule

  • Clearer function vs method call site

  • More available names for parameters, functions, and variables (which is the names you'll be writing the most)

    • My proposal:

      • "If @typeOf(x) == type or x is a function returning type then x should be TitleCase"
    • Current (after namespace changes):

      • "If @typeOf(x) == type and std.meta.fields(x).len != 0 or x is a function returning y where @typeOf(y) == type and std.meta.fields(y).len != 0, then x should be TitleCase"

On a side note, the rule as listed "current" doesn't appear to match Zig online docs. I read the docs like this:

  • "If @typeOf(x) == type and std.meta.fields(x).len != 0 or x is a function returning y where @typeOf(y) == type, then x should be TitleCase"

Thank you Hejsil for responding. I'm pretty sure there isn't much more I can say except that I agree there is significant subjectivity. It's not clear to me that I'm late to the game on providing input, but regardless, I'm happy with the way Zig is progressing and whichever way this goes it won't be a show-stopper for me.

@Hejsil
Copy link
Contributor Author

Hejsil commented Mar 5, 2019

@mikdusan You seem to have a good idea of common practices in these areas of writing. I would agree that I too tend to see this TAR style more often than not (I think, I don't keep track) when reading 'tech-print', but it doesn't seem to be a standard (I can't find any information about TAR). Wikipedia has a section about caps styles, and from what I can read here, the choice is mostly stylistic.

I don't have the same source for abbreviations, but I think it's mostly a stylistic choice as well. I'd never write Util and Interop in all-caps.

I also know that C# uses the proposed style for namespaces, but most languages similar to Zig uses snake_case (Rust, C++, Go).

Edit: Also, you're not late at all on this issue. Until it has been accepted and applied, the discussion can and will continue :)

@andrewrk
Copy link
Member

andrewrk commented Mar 5, 2019

I don't have a concrete counter proposal to this issue at the moment, but I want to say that I agree with @mikdusan's breakdown in #1884 (comment). I think it clearly demonstrates the pros/cons of status quo as well as this proposal.

In particular, there are a few identifier names in the standard library that feel "wrong" according to status quo style guide (things like XmlParser, especially when "XML" is one letter such as "C"), and it's tempting to break away from the style guide. However I'm also not a fan of Std.Io. I want to encourage namespaces, and offering a quick way to type them is one way to do that. I fear the annoyance of the shift key will make more people want to use usingnamespace (#2014).

@andrewrk
Copy link
Member

andrewrk commented Mar 8, 2019

Here's an example from standard library that is awkward:

    pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
        self.verbose_cc = value;
    }

The lower case one looks good. "Verbose C Compilation". cc is a command on most systems.

The camelCase one looks wrong, and setVerboseCc looks even wronger 👎

But as is this example represents cognitive dissonance, because if the fn name and the field were to match, then it would have to be setVerboseCc/set_verbose_cc or setVerboseCC/set_verbose_c_c. I don't want people (myself included) to get distracted by cognitive dissonance of naming things. So that's a reason why status quo is flawed. Note, I don't necessarily consider this proposal to be an adequate solution. Just admitting there is a problem.

@mikdusan
Copy link
Member

mikdusan commented Mar 9, 2019

setVerboseCc/set_verbose_cc or setVerboseCC/set_verbose_c_c

more 2¢; in consideration of article Camel case:

  • camelCase is the practice of writing phrases such that each word or abbreviation in the middle of the phrase begins with a capital letter
  • snake_case uses underscores interspersed with lowercase letters
  • camels (meaning both UpperCamelCase and LowerCamelCase) and snake_case delineate on words or abbreviations (acronyms fall under abbreviations)

and those are some pretty good general rules. But it's not clear enough for more complex cases that coding runs into. The following guidance is more or less my preference style to clear things up:

  • prefer to keep abbreviations/acronyms consistent-case
  • for camels prefer to add underscore delineation when 2 or more abbreviations are adjacent
  • for camels sometimes an abbreviation like exe works fine classified as a word; usually seen prefixing real words
delineation tokenized types callables other note
set, verbose, CC word, word, acronym setVerboseCC set_verbose_cc
IR, builder acronym, word IRBuilder irBuilder ir_builder UpperCamelCase acronym+word is usually ok
LLVM, MC, JIT, compiler, options acronym, acronym, acronym, word, word LLVM_MC_JIT_Compiler llvm_MC_JIT_Compiler llvm_mc_jit_compiler camels needed help with consecutive acronyms
compile, exe word, word CompileExe compileExe compile_exe exe treated as word

These styles become much easier to visualize with a table. A good style guide would show simple, medium and complex examples to help readers reach the same naming conclusions.

@Akuli
Copy link
Contributor

Akuli commented Mar 10, 2019

I think Std.Os.Path.resolve is noticably slower and more painful to write than std.os.path.resolve, at least on my keyboard layout (I don't have to press shift to get a dot, but I do need it for uppercase letters). That's more noticable than "looks nice" IMO. It doesn't matter so much for other things than namespace names, because they contain less uppercase letters.

Some stdlib module names were TitleCase in Python 2, but they were all changed to lowercase in Python 3. Most other languages use lowercase names for namespaces too, so maybe people will find it more familiar. Also, Thing.func(&instance) looks to me like Thing is a struct, and func is a function defined in it, and not at all like Thing is a namespace.

@nodefish
Copy link

nodefish commented Apr 17, 2019

Could a scope-resolution operator e.g. :: be introduced for accessing "static" struct members (including nested struct definitions)? Type definitions are comptime-only, roughly equivalent to "static" in other languages so it may be semantically justified to have a separate operator for them.

const std = @import("std");

pub fn main() !void {
    // If this program is run without stdout attached, exit with an error.
    const stdout_file = try std::io::getStdOut(); // getStdOut() accessed via :: because it doesn't take an implied *self param
    // If this program encounters pipe failure when printing to stdout, exit
    // with an error.
    try stdout_file.write("Hello, world!\n");
}

Pros:

  • Makes it explicit we are calling a function over a "method", or accessing const/"static" member over an instance member, which is important information when writing code
  • Namespacing is supported and becomes explicit
  • Keeps minimalist status quo naming convention
  • Easy to type

Cons:

  • Doesn't free up reserved words
  • Adds operator (although I'd argue the distinction it provides is important for an explicitness-first language like zig)

@andrewrk andrewrk modified the milestones: 0.5.0, 0.6.0 Aug 22, 2019
@marler8997
Copy link
Contributor

Why was this closed? Was it rejected? If so, what was the reason? I just want to make sure I'm staying up to date on what's going on with Zig and I feel like I missed something here.

@andrewrk
Copy link
Member

Yes, if a proposal is closed that means it is no longer being considered. Have a look at the comments in this thread. @mikdusan's comments in particular are quite clear.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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

9 participants