POLL: Remove usingnamespace

…meanwhile stdlib already does not contain pub usingnamespace @import() things.
grep -rI usingnamespace /opt/zig-0.14/lib/std/ shows only compilation stuff (.@"usingnamespace") (and I have not so fresh version, 0.14.0-dev.872+a60810b5a)

For comparison, grep -rI usingnamespace /opt/zig-0.XX/lib/std/ | grep pub for

  • XX = 0.13 - one entry
  • XX = 0.12 - also one entry
  • XX = 0.11 - 52 entries
  • XX = 0.10 - 64 entries

so what this poll was for? :slight_smile:

Just trying to gauge community opinion on the removal of a feature - it looks like that’s where the Zig project is heading but I was curious about what the general stance is on the subject here on the forum.

4 Likes

usingnamespace seems like a useful tool and it’s fun to play around with, but I never really found myself in the situation where it actually makes sense to use it in practice. It always seems too annoying to work with compared to the alternatives.
So overall I think it would make sense to just simplify the language and compiler here.

7 Likes

I voted to remove simply because it makes hunting through source code more difficult and the current state of Zig makes hunting through source code very essential.

1 Like

I voted to remove usingnamespace - personally I find that alternatives are cleaner in general, and I don’t think that the use-case for mixins is really important, as I personally disagree with mixins (in most cases). I haven’t had a lot of zig experience (my current local projects folder measures ~60k LOC of zig code), but from that code I also don’t have one usingnamespace token.

To be fair, I don’t consider opinions particularly relevant in this matter. As users, we can share how changes affect us, but most of us aren’t maintainers or direct contributors to the language. FOSS isn’t a democracy opinions don’t carry equal weight. Those who contribute the most should have the final word.

I think the poll is meant to spark conversation and gauge general sentiment, which I’m sure you can find useful, but expressing agreement or disagreement with the changes won’t have much impact.

The maintainers believe those changes are necessary for their vision of the language, and they’ll proceed regardless of our sentiments. Of course, they should consider user feedback and experiences, but most people aren’t knowledgeable about how these changes affect the internals of the compiler.

Ultimately, our opinions are just that opinions and I think there is some values in this kind of post, because we are able to see everyone’s opinion and experiences, even solutions, which in itself seems valuable.

5 Likes

As you say, zig is not designed by committee (thank goodness), so a poll carries little actual weight. Also, there are already a large number of informed opinions for removing (or not removing) usingnamespace in the original github issue.

So I see a poll as potentially useful, not to sway opinions on the techical merits of the move, but to gauge the level of “community outrage” that such a large breaking change might inspire.

3 Likes

usingnamespace is way too easy to misuse and is in conflict with Zig’s “low overhead to reading code” goal.

As a concrete example of how usingnamespace has made things unnecessarily complicated in the past, look at std.EnumMap from 0.11.0, which was defined using a mixin pattern, and consider the amount of cognitive effort it takes to figure out what the actual shape of the resulting type is compared to the much simplified std.EnumMap of today. For one of the first programs I ever wrote in Zig I needed to use these enum data structures, and as a beginner it took me a significant amount of effort even just to figure out which methods were exported (I hadn’t yet discovered zls at that time).

The current main legitimate use of the feature is pub usingnamespace @cImport(...), but that one will soon no longer be relevant since @cImport is going away in favor of invoking zig translate-c via the build system. For almost any other use case, usingnamespace is the strictly worse (in terms of readability and discoverability) alternative to just re-exporing declarations one by one. You have to write a bit more boilerplate, but in return you get more immediately readable code that clearly tells the reader which symbols came from where. Code is read more often than written, so this seems like an acceptable tradeoff.

6 Likes

As long as we are not reaching 1.0 I believe the makers of Zig should do whatever they think is best!

I saw some things on youtube and read some articles. I have quite a lot of confidence in their vision (which resembles in general my own view).
After a few days of Zig (after a Rust deception, I really tried) I got adicted already.
Very clear. Very elegant.

2 Likes

Such polls are a terrible way to make decisions. Most people voting to remove have never used it, don’t know what’s for, have mistaken beliefs about it (no, it doesn’t make incremental compilation harder–Andrew changed its semantics to avoid that but unfortunately didn’t change the name … there’s an open issue for doing so), and/or don’t know or don’t care about the harm that removing it can do.

I have a library with a large number of functions organized into numerous files, but all the function names appear in a flat namespace. Removing the keyword would require me to either merge all the files into one, enumerate declarations for all functions into one file (tedious and error prone violation of DRY), or change my API to use two level names that don’t help the user.

If it’s dropped then I’ll probably migrate to some other language that provides this ability via public import or some similar syntax.

I think the poll was done to raise awareness about usingnamespace possible removal and to get ideas that might lead to other plans.
Ziggit is a zig users community. Decisions for zig directions are not made here.

5 Likes

Alright, here comes an odd solution to our problems: A user-space implementation of usingnamespace (well almost):

const std = @import("std");

pub fn MergeNamespacesType(namespaces: []const type) type {
    var fields: []const std.builtin.Type.StructField = &.{};
    for(namespaces) |T| {
        for(@typeInfo(T).@"struct".decls) |decl| {
            fields = fields ++ [1]std.builtin.Type.StructField{.{
                .name = decl.name,
                .type = @TypeOf(@field(T, decl.name)),
                .default_value = &@field(T, decl.name),
                .is_comptime = true,
                .alignment = @alignOf(@TypeOf(@field(T, decl.name))),
            }};
        }
    }
    return @Type(.{
        .@"struct" = std.builtin.Type.Struct{
            .decls = &.{},
            .fields = fields,
            .is_tuple = false,
            .layout = .auto
        }
    });
}

pub fn mergeNamespaces(namespaces: []const type) MergeNamespacesType(namespaces) {
    return .{};
}

pub fn main() void { // MARK: main()
    const X = struct {
        pub fn a() void {}
        pub const b: u32 = 1;
    };
    const Y = struct {
        pub const c: u32 = 24;
    };
    const Z = struct {
        pub fn d(x: u32) u32 {return x + 1;}
    };
    const AllYourNamespacesAreBelongToUs = mergeNamespaces(&.{X, Y, Z});
    AllYourNamespacesAreBelongToUs.a();
    std.debug.assert(AllYourNamespacesAreBelongToUs.b == 1);
    std.debug.assert(AllYourNamespacesAreBelongToUs.c == 24);
    std.debug.assert(AllYourNamespacesAreBelongToUs.d(13) == 14);
}

Ok, to be fair it’s a bit more clunky, since you cannot merge namespaces at a file scope, and it won’t work in some cases, like for global variables. But this should be good enough to cover the simple library use-case (of course before using this, a safe-guard for global variables should be added, so they won’t get accidentally included).

3 Likes

Or you might be able to generate the file via the build system, or use some other mechanism. As the language changes some things may have to be done differently, but maybe there are other ways that even turn out better in the long run.

I think we shouldn’t be too attached to one particular way to achieve something and instead try to find other solutions for making certain things easier (possibly including new ideas for language features, that weren’t already rejected for various reasons).

If you want to make someone aware of something then just tell them. If you want ideas, then ask for them.

I can’t see how your comment addresses anything I wrote.

Having read through the lengthy Zig issue, I see many upvoted use cases for usingnamespace and complaints about the suggested replacements, and many downvotes for the rationalizations by the author of the proposal. I suspect that those votes are coming from people who would be impacted by the change.

The apparent reason for the proposal is to avoid difficulties with incremental compilation, but those difficulties could be avoided by putting restrictions on the expression.

I also see a repeated claim from the author of the proposal that “namespaces are good”, but they aren’t always good and several people have given examples where imposing them is bad. This sort of repeated claim is what might be called an “argument smell”.

Generating the file using a separate program was mentioned in the issue discussion but it has clear downsides. As for “some other mechanism”, the whole point is that they don’t exist. The answer from the author of the proposal was “namespaces are good”, which is not a “mechanism”.

Anyway, I hadn’t read the Zig issue thoroughly before commenting here and now I have – I think it has been discussed adequately there, with numerous uses cases including by authors of 250K LOC projects who oppose the removal, so I don’t think any further discussion here is useful, and I think I should have just responded in the Zig issue rather than here … my mistake. I won’t comment here further.

Your comments made me realize this proposal would codify that files = namespaces in zig, but is this really a bad thing?

If you (the library developer) feel encouraged to divide these functions into files (effectively namespaces), wouldn’t your users want that too? What makes an extremely large, flat namespace useful to library authors or users? Less / shorter import statements at the top of the user’s files?

As a library author I might separate something into multiple files because I find it impossible to navigate 5000 lines of zig stuffed in a single file.
For a language that has zen of prefer reading code over writing, removing usingnamespace would directly cause 5000+loc files to become more common.

3 Likes

The proposed alternative is to create a namespace which re-exports each symbol individually. This is something which the standard library already does. More tedious to write, but explicit as to where each declaration comes from when reading.

4 Likes

Also, maybe not crucial, but having everything in a single namespace means endless scrolling through IDE popups to find the item you need.

1 Like

First, it doesn’t codify any such thing. e.g., one of the examples in the Zig issue had each function in a separate file … each such function had one line in the namespace file importing the function from its file. (usingnamespace was not needed in that case.) Different developers can choose different approaches. Second, forcing such things on people is a bad thing. Third, the names that I as a developer pick for my file organization are not always well-thought out and change from time to time, and I sometimes move functions between files. Equating file names to namespaces would make such changes in the implementation be API changes.

I said I wouldn’t comment further here but I felt that this direct reply to me warranted a response. I suggest and request that any further comments be added to the thread but not directed to me … this truly is my last comment here.

1 Like