Open SeanTheGleaming opened 5 months ago
The mixin approach is not viable because the contents of @import("builtin.zig")
may differ between modules in one Zig compilation. And yes, for the record, there is a hard ban on introducing uses of usingnamespace
into the standard library for now.
I have no idea how common is this approach:
const std = @import("std");
const math = std.math;
const os = std.os;
but I don't find this pattern convincing. I'd argue that, as a general rule, writing subspaces of std
out explicitly has a positive impact on the readability of the code.
Anecdote from the point of view of a user:
I started writing Zig about a year and a half ago and I distinctly remember that for the week or so I found the term "builtin" very overloaded and confusing, repeatedly mistaking @import("std").builtin
for @import("builtin")
until I eventually found my bearings.
The name std.builtin
makes sense from the point of view that it defines and exposes interfaces that support @
-prefixed built-in functions.
The generated builtin
module that provides information about the compiler and target, however, I think has a misleading name. Aside from the fact that it's always automatically generated by the compiler, there is nothing especially "built-in" about it, and it is functionally more similar to something like std.Build.Options
than the rule-breaking "you can't implement this in user space" functionality usually associated with the term. The documentation refers to builtin
as "compile variables", so I wonder if using a different name like compile_vars
, build_env
, comptime_env
, compilation
, etc. would help convey its purpose more clearly and make it less likely to get mixed up with std.builtin
.
Slightly renaming is preferable.
I propose add a 's' to one of them
const builtin = @import("builtin");
const builtins = @import("std").builtins;
Rename @import("builtin");
to @import("builtin_info");
or @import("builtin_constants");
We can do both 1. and 2. above.
Personally, I think they still deserve separate names since they contain separate things -- @import("build")
contains constants related to the build configuration, while @import("std").builtin
contains types/variables/functions provided by the language. (Naming mentioned... it's bikeshedding time!)
The name "builtin" also becomes confusing when you consider that Zig also has a concept of "builtin functions" (the @
-prefixed functions)
I propose @import("builtin")
to be renamed to @import("build")
, and @import("std").builtin
to be renamed to @import("std").zig
. I think the @
-functions should continue being called "builtin functions". I think these three names effectively communicate what they are pretty well!
const build = @import("build");
const zig = @import("std").zig;
// some common usages of both of these namespaces
if (build.mode == zig.OptimizeMode.Debug) { ... } // btw, we should probably fix the casing here...
if (build.is_test) { ... }
pub fn Wrapper(comptime T: type) type {
return @Type(zig.Type.Struct{ ... });
}
the general solution is to not do const builtin = std.builtin;
. fully qualifying your names makes for much more readable code.
I agree, typically I don't actually do stuff like const builtin = std.builtin;
(except for namespaces in my own code sometimes), was just doing that for the example I guess. My main thing was "if we are renaming it, let's pick a name that makes it more distinct"
I agree that creating random aliases for parts of the standard library should be discouraged, but the reality is that people will do it to some degree, regardless of the disapproval. I don't think it should be possible to confuse these names in the first place. Personally, the first idea that comes to mind, is renaming std.builtin
to std.internal
. I'm not experienced enough with the compiler source to recognize any naming issues in-compiler. However, from the perspective of a user, in my opinion, "internal" does a better job at communicating that its contents are related to fundamental language constructs, not parameters for a specific compilation target.
My issue with internal
is that the name makes it feel like something that's unsafe and shouldn't be used, when in reality it has a lot of useful utilities in it that are fine to use. The reason I picked that to be std.zig
is because the package contains functions/constructs pertaining to the zig language. I think std.lang
could also be a good candidate.
Also -- I don't think the whole "making aliases for standard library" part is really the pain point here. The pain point IMO is that std.builtin
doesn't effectively communicate what's inside of the package, especially when @import("builtin")
and "builtin functions" both exist, and both mean completely different things.
(Another option could honestly be to just merge std.meta
and std.builtin
considering that they're both packages which contain metaprogramming utilities... although std.builtin
does have a few things that wouldn't make sense in std.meta
, like the std.builtin.panicXyz
methods)
I do understand how internal
could be interpreted that way. I'm pretty sure std.zig
already exists, but I have to say, I actually really like std.lang
as a potential candidate. Just throwing some extra darts at the wall, what about std.core
or std.primitive
?
We could only rename @import("builtin");
to
@import("build_info");
or
@import("build_constants");
It feels weird that the Zig language is currently tied to the standard library by std.builtin
.
If status quo is maintained the Zig language specification is going to have to define the contents of the std.builtin
module of the standard library, instead of having no references to the standard library in the language spec at all.
The Zig language could be made independent of the standard library by:
@import("builtin")
to something that makes it clear that this is per build information like @import("build_info");
std.builtin
from the stdlib and instead the compiler provides it as @import("builtin")
or even @import("builtins")
so code still referring to @import("builtin")
failsHow about @import("me")
? It's recognizable (machine/module environment, just me etc.) and very short, which should help people to use it correctly and respect different module settings in different callsites:
switch (@import("me").mode) {
// ...
}
It feels weird that the Zig language is currently tied to the standard library by
std.builtin
.If status quo is maintained the Zig language specification is going to have to define the contents of the
std.builtin
module of the standard library, instead of having no references to the standard library in the language spec at all.The Zig language could be made independent of the standard library by:
* Changing `@import("builtin")` to something that makes it clear that this is per build information like `@import("build_info");` * Removing `std.builtin` from the stdlib and instead the compiler provides it as `@import("builtin")` or even `@import("builtins")` so code still referring to `@import("builtin")` fails
Even though I now agree that fully writing out the namespace you want is a sufficient solution to the original issue, I think this reply is onto something. I like this idea, for one both because it solves my original problem in a clean and readable way, but also because it solves a much larger problem I hadn't even considered when I original wrote this issue.
The Zig language depending on the namespace of std.builtin
(or really just the standard library in general) is problematic for a couple reasons. As @leecannon mentioned, it would mean defining at least part of the standard library in the language spec. Additionally, it could create confusion and ambiguity in cases where multiple modules in the same compilation have the same value for std.builtin
, but different values / behaviors for @import("builtin")
/ target specific language features. For example, it could create a conflict on whether std.builtin.VaList == @TypeOf(@cVaStart())
between multiple modules of different targets.
To try to summarize simply, if the contents of @import("builtin")
can change between modules, but the contents of std.builtin
cannot, then this can create conflict and ambiguity down to the language itself. As @leecannon suggested, it might be a good idea to divorce std.builtin
from std
altogether. This would have the added benefit of completely removing the language's dependence on the @import("std")
namespace
Problem
A common pattern when making an import is to make a top level declaration importing that module, and then make top level declarations for the namespaces of that module which you use. For example:
This does create a small issue though. Consider the a use case where I will be using both the
std.builtin
namespace and the@import("builtin")
module. This creates a conflict when using the widely accepted pattern for imports:This would normally be pretty innocuous, but the problem lies in that for real-world cases where
@import("builtin")
is used, it is usually used in tandem withstd.builtin
, since the two deal with often-intersecting use cases. For example, consider a use case where I want to do metaprogramming withstd.builtin.Type
, but also want to check whether libc is linked via@import("builtin").link_libc
, or if I want to access@import("builtin").target
and also usestd.builtin.panicOutOfBounds
.Proposed Solutions
1. Rename
std.builtin
(Or maybe rename@import("builtin")
This is a simple enough solution, but needless to say, this would be a breaking change which would affect many projects. You could also apply the same idea and rename the
@import("builtin")
module, which would have similar implications. On top of fixing the problem, another benefit of this solution is that, at least in my opinion, "builtin" is a bit of a misleading name for both the module and the std namespace. A new name may make their purposes more clear. For example,@import("builtin")
could be changed to@import("env")
to more clearly indicate that it contains declarations regarding the environment which the code is being compiled under2. Mix
@import("builtin")
intostd.builtin
This is the solution I am most inclined to implementing. All it would require is adding the line
pub usingnamespace @import("builtin");
intolib/std/builtin.zig
. This would eliminate the need to use@import("builtin")
outside of the standard library, reducing the earlier example to the following while breaking no existing codeAll it would require is adding the line
pub usingnamespace @import("builtin");
intolib/std/builtin.zig
. And if we don't want ausingnamespace
statement, it would be easy enough to just manually declare each value with how small@import("builtin")
is. So the two ways to do this would are:1.
2.