Allowing .packed, .extern, .struct, .fn, etc. (no @"...")

No language I know of prohibits the use of keywords as field names then proceeds to force end-users to force end-users to use the said keywords as field names. My proposal is entirely a reaction to a decision made by others. I have no problem with .Struct staying .Struct. For the record I do not want this change. What I’m suggesting is a way to mitigate the negative consequences of this change.

Frankly, I don’t know how anyone can insist on using struct as a field name yet simultaneously insist that .struct should remain illegal.

For what it’s worth, I don’t think lowercasing the type enum collection was a great idea. I get how it happened: Zig convention for enums used to be uppercase, and now it’s lowercase, this brings the Type union into alignment with that convention.

But Types Are UpperCase Already. So it isn’t at all weird that the enums of types were uppercase also, and frankly it just makes work for people on what amounts to aesthetic grounds.

But it’s a done deal, and I did not and do not see the point in a futile burn of social capital complaining about it. I see the other side of the argument, I wasn’t there when it was decided, and “amounts to aesthetic grounds” goes both ways.

However, I don’t think that changing how the field and enum rules work is a useful thing to do, on its own merits. Furthermore it is a bizarre overreaction to use this particular change as grounds for that one.

I don’t think your opinion that this is some sort of disaster which will put people off from the language will prove to be a widely shared one.

When you express yourself this way:

You’re using language which assumes the conclusion you would like others to draw. Every user of a language is forced to do what the language does or it won’t compile. I’m forced to type try array.ensureUnusedCapacity(1) if I want to ensure that the array has at least one slot of unused capacity. Everything is like this. If I had wanted in 0.13 to switch on @typeInfo and use .int, well, I would have been forced not to.

Aesthetically, yes, I see the point you’re raising. This:

switch (some_typeinfo) {
    .int => ...,
    .float => ...,
    .@"struct" => ...,
}

It is not aesthetically regular. It isn’t smooth. It has a weird thing about it.

But I am baffled by the force of your conviction that this represents some existential threat to Zig’s existence, that newcomers to the language will be repelled and flee in disgust. I just don’t see it that way. I see it as a minor detail that people will instantly grasp and move on from. I would anticipate at least as much “oh cool you can use anything as an identifier with an @"string" huh, neat” as “hmm that doesn’t look nice”, and approximately no existential horror and staunch refusal to ever touch the language again.

1 Like

Choosing a programming language is a massive commitment. Small cosmetic issues have a way of destroying confidence. A typo in your resume, for instance, will easily cost you the chance of landing an interview regardless of your qualifications.

Instead of changing the language, another way we can deal with the code ugliness issue is to hide the problematic std.builtin.Type behind functions in std.meta:

pub fn isStruct(comptime T: type) bool {
    return @typeInfo(T) == .@"struct";
}

pub fn structInfo(comptime T: type) std.builtin.Type.Struct {
    return @typeInfo(T).@"struct";
}

pub fn Struct(info: std.builtin.Type.Struct) type {
    return @Type(.{ .@"struct" = info });
}

// ...etc

These functions would take care of common scenarios where you’re only dealing with one or two types:

pub fn half(arg: anytype) @TypeOf(arg) {
    const T = @Typeof(Arg);
    if (std.meta.isInt(T) or std.meta.isFloat(T)) {
        return arg / 2;
    } else {
        @compileError("Numeric value expected");
    }
}

For scenarios where a switch is more appropriate, we provide the following function:

pub fn typeInfo(comptime T: type) active_field_type: {
    const info_union = @typeInfo(T);
    const tag = std.meta.activeTag(info_union);
    const info = @field(info_union, @tagName(tag));
    break :active_field_type @TypeOf(info);
} {
    const info_union = @typeInfo(T);
    const tag = std.meta.activeTag(info_union);
    const info = @field(info_union, @tagName(tag));
    return info;
}

The end user would then switch on the type of struct returned:

const Type = std.builtin.Type;

pub fn boo(arg: anytype) void {
    const T = @Typeof(Arg);
    const info = std.meta.typeInfo(T);
    switch (@TypeOf(info)) {
        Type.Struct => {
            // ...
        },
        Type.Array => {
            // ...
        },
        else => {},
    }
}

I think the above construct would be comprehensible to even newcomers. Knowledge of enum literals, tagged union to enum literal cast, and variable capturing are no longer required. It’s also clear where the struct types involved are located in the namespace.

This scheme requires the addition of new structs for bool, comptime_float, and others that are getting a void from std.builtin.Type currently. I don’t think that’s a major change.

1 Like

I’ve written a few functions like that, here and there. Even put them in a meta namespace along with the rest of std.meta, using usingnamespace which I will be upset if the crew ends up ditching.

This sort of thing seems like a good basis for a library/package. If it proves popular it might end up in std, it’s happened before.

We just have different perspectives on the keyword enums thing. That’s fine. From mine it’s mostly an example of a cool thing which Zig does, one which is relatively uncommon and empowers a lot of useful things.

I don’t expect most beginners will encounter switching on type enums immediately, and those who do are evaluating Zig because of its metaprogramming and reflection potential. Those are more likely to appreciate the clever solution to the use/mention distinction, more than they are to be seriously turned off by the irregularity.

Neither of us like the change all that much, but that won’t be a factor for users who never saw the original way of doing it. I’m trying not to let it color my perceptions, and to work backward to how I would have responded to seeing the type enums when first learning Zig, if they had already looked the way they do. I’m pretty sure my reaction would have been “oh, any string can be an identifier? that’s brilliant actually” which is how I responded to the @"any string you want" syntax when I did discover it.

1 Like