ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
33.51k stars 2.45k forks source link

Rename `usingnamespace` to `mixin` #9861

Open ifreund opened 2 years ago

ifreund commented 2 years ago

The semantics of usingnamespace were changed in #9618 as per proposal #9629. However, the keyword itself was not changed and it was decided to leave that for a separate proposal.

To reiterate my comment on #9629, I think the originally proposed includenamespace isn't perfect as not everything in the target namespace is included, only the public declarations.

I think using mixin as the new keyword would be more clear and concise. Mixins are already a well known concept in programming (wikipedia) and the revised usingnamespace semantics map quite well to the generally understood concept of a mixin.

I also proposed using includedecls or includedeclarations in the original thread, however I find these ugly compared to mixin and don't think they give a clearer idea of the semantics.

As this is a rather zig-specific concept, people new to zig will almost certainly need to read the documentation to learn the exact semantics of this keyword no matter what we choose.

nmichaels commented 2 years ago

I like mixin better than any of the alternatives you listed, but it implies something false to me. When I see mixin it makes me think that the mixed-in things will be available in the local namespace. reexport is awkward but closer to the intent. transship? include?

This feels a little bit bike-sheddy, but the feature's implemented and good names are really important. If we take a name for a concept with which people are familiar (like using namespace) then users will be surprised when it has different semantics from what they expect. If it's an entirely unfamiliar name, they'll have to look up what it does, which is probably what we want. The balance, I think, is finding a name that's unfamiliar enough to cause transplants from other languages to look it up, while being intuitive enough that Zig programmers can remember it easily.

InKryption commented 2 years ago

More bike-shedding here, but how about forward, forwarddecls or forwardnamespace? Being that the new semantics are closer to forwarding declarations, as opposed to mixing them in with the present declarations, or including them.

rohlem commented 2 years ago

I've never used a language with a mixin keyword, but Wikipedia says many languages do different things with it, so I'm not convinced that the word implies influencing identifier lookup. They are available in the namespace, via indirection of @This().

To explicitly show only pub things are included, we could put it in the name: includepub / mixinpub . As far as I understand it, that is no longer the case in stage2? Non-public declarations equally qualify, as long as they are within the same file (how pub works in general).

adopt/adoptdeclarations/adoptdecls would also sound understandable to me.

ghost commented 2 years ago

+1 for forwarddecls. It's kinda ugly, but describes the semantics precisely. Since this is not a frequently used feature, a long and descriptive keyword should be better than a short but somewhat cryptic one.

benrob0329 commented 2 years ago

I think that mixin is short and to the point. Reading the summary of what a mixin is, it perfectly describes the intended use case, mixing in the definitions from another source. Its less awkward than most other suggestions, and being short encourages use.

Guigui220D commented 2 years ago

@benrob0329 I think the goal at some point was to have a long word for it to discourage its use and not encourage it though

benrob0329 commented 2 years ago

@Guigui220D Do you know why it's use would be discouraged? The intended use-case seems reasonable enough, though I suppose it does add some hidden complexity.

kenaryn commented 2 years ago

What about nonlocal or outerspace?

jibal commented 2 years ago

How about calling it mixindeclarations ? Or mixindeclarationsof ?

Hsad commented 2 years ago

I like the logic of adopt, it implies the superseding behavior of needing to label the namespace to access it. Sort of like a new family name. Something like AdoptNames, or AdoptDef could do a better job of hinting towards the actual use case. Usurp, Clone, Sample, Inherit, Use, Reference/Refencing, Take, Utilize, + Names/Def, etc, could be other variations with a similar train of thought.

nektro commented 2 years ago

I like usingnamespace being long, and "namespace" being in the name accurately reflects that only the decls are being imported

jibal commented 2 years ago

But the decls aren't imported ... not any more; they are added ("mixed in") to the container and available to its users (by qualifying the decls with the name of the container), but they aren't available to the code within it (without qualification).

BTW, there seems to be a bug ... although the symbols are not imported, the duplicate definition checker thinks they are, so you can't define the symbols that you've mixed in.

Edit: I tried to create a test case for the above, but it worked fine ... maybe it's been fixed or I'm misremembering the problem. operator error ... the bug is still there.

igor84 commented 2 years ago

Based on the comments here I couldn't quite grasp why mixin is not a perfect choice, but after playing around with it this example made me realize:

fn SA(comptime Self: type) type {
    return struct {
        a: u8 = 80,

        pub fn add(self: Self, b: u8) u8 {
            return self.a + b;
        }
    };
}

const SB = struct {
    c: u8 = 80,
    pub usingnamespace SA(@This());
};

This example produces a compile error no member 'a' in struct 'SB' on return self.a + b;. That is because usingnamespace only includes the declarations (functions and consts) but not fields from SA into SB. Note that if you changed that line to return self.c + b; it would compile since Self in SA will actually refer to SB.

To me the word mixin implies mixing in fields should also work since I first learned about the concept in DLang. But, wikipedia is a bit confusion on the terms. The mixin page actually describes mixin as something that only mixes in additional methods which actually is what is happening here. On the other hand the page about trait specifically describes the difference from mixing with this sentence:

In contrast, mixins include full method definitions and may also carry state through member variable, while traits usually don't.

I still like the choice of mixin keyword the most but I do understand now why it is not perfect.

daneelsan commented 2 years ago

Because I didn't see this above I'll propose: usingdeclarations. To me, declarations is a more self-explaining word that namespace. If using is not of your preference, how about splicedeclarations (https://dictionary.cambridge.org/dictionary/english/splice).

ominitay commented 2 years ago

forwarddecls, or forwarddeclarations, as suggested by @InKryption more adequately describes the behaviour than usingdeclarations

nektro commented 2 years ago

namespace is already the name used internally for the list of a container's declarations. the name is fine

ominitay commented 2 years ago

@nektro The term using isn't great, can be misleading in this context though. This is obviously nitpicking though lol.

jibal commented 2 years ago

My suggestion of mixindeclarations got a lot of downvotes even though it is more accurate than many of the other proposals and clearer and less ambiguous than simply "mixin" which is what this issue proposes ... but on reconsideration I think that forwarddeclarations is better ... mixin, like using, has the wrong implication as @nmichaels explained above.

namespace is already the name used internally for the list of a container's declarations. the name is fine

The problem isn't so much with namespace (although only the public declarations are forwarded) as it is with using ... as of #9618 (implementing #9629) the code that includes the keyword does not and cannot use the declarations from that namespace ... they are only available to the importers of the code that contains the keyword. (I made the same point back on June 3, in response to " the name accurately reflects that only the decls are being imported" which is not accurate--the decls are exported, not imported. Please read ifreund's proposal up top ... the whole reason for it is that the semantics of the keyword changed.)

codethief commented 1 year ago

but on reconsideration I think that forwarddeclarations is better ... mixin, like using, has the wrong implication as @nmichaels explained above.

I agree with @jibal's assessment here. In fact, it took me the better part of an hour now (of reading the docs as well as the various Github issues I found here and on DuckDuckGo) to realize that, contrary to its name, usingnamespace does not (or no longer, as of #9629) allow using the members of the given namespace in the current scope (the one that contains the usingnamespace instruction). This is very confusing.

Even if the name will not be changed, it should at least be documented properly.

expikr commented 9 months ago

May I suggest derive? Or deriving, or something along those lines:


pub fn Mat3(comptime T: type) type {
    return struct {
        xx: T = 1, xy: T = 0, xz: T = 0,
        yx: T = 0, yy: T = 1, yz: T = 0,
        zx: T = 0, zy: T = 0, zz: T = 1,
        pub derive 
            Mat3_Impl_Shared(T, @This());
        pub derive if (T==f16 or T==f32 or T==f64 or T==f80 or T==f128) 
            Mat3_Impl_Float(T, @This()) else opaque{};
    };
}

fn Mat3_Impl_Shared(comptime T: type, comptime Self: type) type {
    return opaque {
        const Index = enum {
            xx, xy, xz,
            yx, yy, yz,
            zx, zy, zz,
        };
        pub fn compose(a: Self, b: Self) Self {
            return .{
                .xx = a.xx*b.xx + a.xy*b.yx + a.xz*b.zx,
                .yx = a.yx*b.xx + a.yy*b.yx + a.yz*b.zx,
                .zx = a.zx*b.xx + a.zy*b.yx + a.zz*b.zx,

                .xy = a.xx*b.xy + a.xy*b.yy + a.xz*b.zy,
                .yy = a.yx*b.xy + a.yy*b.yy + a.yz*b.zy,
                .zy = a.zx*b.xy + a.zy*b.yy + a.zz*b.zy,

                .xz = a.xx*b.xz + a.xy*b.yz + a.xz*b.zz,
                .yz = a.yx*b.xz + a.yy*b.yz + a.yz*b.zz,
                .zz = a.zx*b.xz + a.zy*b.yz + a.zz*b.zz,
            };
        }
        pub fn cof(a: Self, index: Index) T {
            return switch (index) {
                .xx => a.zz*a.yy - a.yz*a.zy, .xy => a.zx*a.yz - a.yx*a.zz, .xz => a.zy*a.yx - a.yy*a.zx ,
                .yx => a.xz*a.zy - a.zz*a.xy, .yy => a.xx*a.zz - a.zx*a.xz, .yz => a.xy*a.zx - a.zy*a.xx ,
                .zx => a.yz*a.xy - a.xz*a.yy, .zy => a.yx*a.xz - a.xx*a.yz, .zz => a.yy*a.xx - a.xy*a.yx ,
            };
        }
        pub fn det(a: Self) T {
            return a.xx*a.cof(.xx) +
                   a.xy*a.cof(.xy) +
                   a.xz*a.cof(.xz) ;
        }
    };
}

fn Mat3_Impl_Float(comptime T: type, comptime Self: type) type {
    return opaque {
        pub fn inv(a: Self) ?Self {
            const detA: T = a.det();
            return if (detA == 0) null else .{
                .xx=a.cof(.xx)/detA, .xy=a.cof(.yx)/detA, .xz=a.cof(.zx)/detA,
                .yx=a.cof(.xy)/detA, .yy=a.cof(.yy)/detA, .yz=a.cof(.zy)/detA,
                .zx=a.cof(.xz)/detA, .zy=a.cof(.yz)/detA, .zz=a.cof(.zz)/detA,
            };
        }
    };
}
jedisct1 commented 9 months ago

May I suggest derive?

I'm not found of it. "derive" doesn't convey the idea that it augments something.

expikr commented 9 months ago

Well, it isn't quite augmenting the Type being referenced (since you can't immediately access it within), rather it's merging the declarations from it (as you can only access it in the final product). Words that properly conveys the shoehorning "tacked-on" sense would be ones like "Impute", "Imbue", or "Infuse" but those seem a tad overmuch. "Inject" might suit it but it seems to have a rather overloaded usage in other places, while "Imitate" only suggests behaviors are replicated and not the comets and vars.

reappropriate might work.

jibal commented 9 months ago

rather it's merging the declarations from it

But it's not ... please see the discussion above. The keyword has very odd semantics ... it exports the symbols from the referenced struct but it doesn't import them ... the code containing the keyword cannot use the symbols. That's the whole reason for this proposed change.

In any case I can't see how "derive" or "deriving" give any indication of what it does, and suggests a relationship that may not hold at all.

ghost commented 9 months ago

Since we're still bikeshedding, how about mixout? It's a mixin that only has an effect outside the current namespace )) Also unusual enough to hit the docs right away, without forming wrong expectations first.

deflock commented 9 months ago

pollute 🤪

jibal commented 9 months ago

None of these suggestions is clearly better than forwarddecls (or forwarddeclarations -- there's no need to be cryptic with this rarely used keyword), suggested over 2 years ago.

expikr commented 9 months ago

derive isn't that cryptic is it?

mlugg commented 9 months ago

derive would be pretty misleading. It gives implications of OO-like behaviours: inheriting fields, and adjusting method types to operate on this type. In reality, all the keyword does is directly include all symbols from another namespace.

jibal commented 9 months ago

derive is wrong in every possible way.

Again, forwarddeclarations is accurate and none of these other suggestions are improvements ... and some are just horrible, with unclear semantics and connotations that simply don't apply.

It might be helpful to look at #9629 to see the sorts of use cases that kept Andrew from removing the keyword altogether ... examples like

// windows.zig
pub usingnamespace @import("std").os.windows;
pub usingnamespace @import("misc.zig");

where it is used to combine decls from various namespaces into a new namespace.

Most helpful though would be to stop bikeshedding this tiny corner of Zig and spend our time more fruitfully. And on that note I'm going to unsubscribe.

jedisct1 commented 9 months ago

How about "merge"?

ghost commented 9 months ago

How about "merge"?

That would not be a good keyword to reserve, IMHO.

nektro commented 9 months ago

usingnamespace is good as is

deflock commented 9 months ago

Okay, brainstorming is going on: involve, mixup, or some re-prefixed reintroduce, reuse, reinject

expikr commented 9 months ago

Append

hsyl20 commented 9 months ago

reexport