← Back to context

Comment by sfpotter

7 days ago

One thing I just can't understand is proactively using the :: syntax. It's sooo ugly with so much unnecessary line noise. Just use a single period! I think one of the best decisions D made was to get of -> and :: and just use . for everything.

I like to quickly at a glance be able to distinguish between "this is a member access on an object" and "this is referencing something in a module".

I like that the compiler can distinguish those two things too, so that I can refer to things within modules even if I happen to also have a local variable with the same name. Go sometimes annoys me because I need to rename a module or a variable just because the syntax doesn't distinguish between the two things.

  • > distinguish between "this is a member access on an object" and "this is referencing something in a module".

    zig: what's the difference?

    • There is an overlap between module and class namespacing through static members and functions. Zig sees this and says "let's only have the latter" (on structs), which means among other things that in Zig a file is a structure. Most other languages says "let's have both".

      In C3 it goes the other way despite having methods, and says "let's not allow static variables or methods".

      This also goes hand in hand in the language approaches between open/close. Zig is very strongly "closed" and C3 is very much "open" (adding methods to types anywhere, appending to modules anywhere etc)

      It's an interesting contrast that leads to very different code layouts.

    • Surely Zig differentiates between, say, a function in a module, and a member on an object? Or are "modules" literally just instances of classes or something?

      9 replies →

`::` simplifies the module vs identifier resolution.

In C3 there is something called "path shortening", allowing you to use `foo::bar()` in place of something like `std::baz::foo::bar()`. To do something similar with `.` is problematic, because you don't know where the path ends. Is `foo.baz.bar()` referring to `foo::baz::bar()` or `foo::baz.bar()` or `foo.baz.bar()`?

  • I would just explicit require pulling `foo` into scope, like Rust does. Though in fairness Rust also uses :: for scope anyway and I don't think it's as bad as sfpotter says.

  • Sure, but in practice I believe most developers would find it intuitive to just type . everywhere.

    It feels more lightweight and consistent, and collisions aren’t super common once you adopt some conventions.

    It’s a tradeoff for sure, but this preference comes from having lived in both worlds.

    • > Sure, but in practice I believe most developers would find it intuitive to just type . everywhere.

      Yeah, but in practice I try not to produce write-only code.

      Code is for reading more than for writing, hence make it easier to read. If it becomes easier to write as a side-effect, then so be it.

  • > `::` simplifies the module vs identifier resolution

    The identifier on the right is looked up in the scope of the identifier on the left. If it resolves to a module, then it's a module. If it resolves to a function, then it's a function. If the left side is a pointer (not a symbol with a scope) then the right side resolves to a member.

    It also makes refactoring much easier - changing a pointer to a reference does not require a global search/replace of -> with .

    • C3 has "path shortening", so for example given `open(...)` in std::io::file is usually used as `file::open(...)`. If we would to write this as `file.open(...)`. Consider now the case of mistyping `open`: `file.openn(...)`. Is this (A) mistyping the function open in module `std::io::file` or is it (B) the global/local `file` is missing from the current scope?

      Also, "io", "file", "random" etc are commonly used variables, so the issue with shadowing is real.

      `File file = file::open(...)` is completely unambiguous and fine. `File file = file.open(...)` on the other hand would be bad.

      If the language had flat modules, or no path shortening, then it would be possible.

      6 replies →

  • This is the kind of thing I don't want to have to think about as a programmer. The compiler should just make it work.

    • But you have to. Ambiguities hint at lack of redundancies that also affects code reading. When you quickly scan something like `file.open` vs `file::open` the former is also more unclear to a reader without more context.

I can understand it, I just hate it. I would prefer confusing dots, a module builtin namespace, rebol backslashes, confusing slashes, anything else.

I like it. It disambiguates when you're referring to a member of an object -- myobj.member vs referring to a global object by its full name -- module::function. I guess you could say the IDE can colour the two differently, but after spending a lot of time working with various code review tools of varying quality, I have come to really appreciate having the program text be as explicit as possible. If you find it so disagreeable, can't you configure your IDE to visually replace that syntax with a single dot and also automatically convert a single dot to two colons when typing a namespace?

I can live with "this::that", but what drives me bonkers is "this :: that", which is what Odin does. Other than that, Odin is an incredible language.

  • that's a constant assignment, completely different. And you're not going to see it 100s of times in a project.

I concur RE: «::». For deep levels of nesting, the level of noise that «::» causes becomes excessive.

When Rust was in its infancy, I maintained a local fork for a while where I modified the parser to use the Ada syntax («'», a single apostrophe) for the same purpose. So

  std::collections::hash_map::HashMap::entry

became

  std'collections'hash_map'HashMap'entry

But I am not convinced that using «.» for two distinct purposes – 1) as a name qualifier, and 2) as a method/data element accessor – is a good idea, for maintaining the semantic clarity is important, i.e. is «e» in «a.b.c.d.e»

  1) A method call; or

  2) An accessor for a deeply nested data structure.

It is not easy to say whilst just glancing upon the code.