← Back to context

Comment by lerno

7 days ago

`::` 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.

  • I’ve lived in both and I prefer ::.

    • I prefer a world where there is no distinction between modules (or namespaces) and object, so '.' it is. (and I'm almost exclusively a C++ programmer).

> `::` 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.

    • > 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?

      D uses a spell checker for undefined identifiers, and the dictionary is all the identifiers in scope. It has about a 50% success rate in guessing which identifier was meant, which is quite good.

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

      If the same identifier is accessible through multiple lookup paths, an error is issued. If a local variable shadows a variable in an outer scope, and error is issued.

      We've developed this over several years, and it works quite well.

      Path shortening can be done with:

          alias open = file.open;
      

      or:

          import io: open;

      2 replies →

    • I feel like path shortening is the issue, and IMO it's an unnecessary feature. I don't think most programmers are bothered by the need to explicitly import what they use. I'd personally prefer to explicitly import what I use, and refer to it in whatever way the import statement would imply.

      In Rust where modules share a namespace with other identifiers, I just pick different variable names, or write my imports so they don't conflict. It's not that big a deal.

      2 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.