r/Zig 8d ago

Discussion: `@importRoot` instead of `@import("root")`

So, let me start by saying I'm pretty new to Zig. I like to learn languages by example, and when reading through examples and source code I noticed `@import("root")` and thought it was importing the corresponding "root.zig" file. After some further research I found out that this is actually a special-cased argument for the compilation root. Honestly I think that's a horrible decision, not only is there special-cased arguments for a compiler-provided method, but its a string argument, so it seems completely magical and non-discoverable.

Imo, Zig already has a specific syntax for special compiler-provided operations: `@func`. So I think a much more sensible and readable version would be `@importRoot`, because if "root" is going to be treated specially then let it at least be openly acknowledged as being special. Same for all of the other special arguments: `@importSelf` and `@importStd`.

But again, I'm new, so let me know if I'm totally off-base here.

28 Upvotes

20 comments sorted by

21

u/chocapix 8d ago

In Zig, @import(target) is basically the same as struct { content-of-target } where target can be:

  • a module name
  • a file name

Using zig's build system you can define any number of modules you want and make them available to your code. It just so happens that, using the same underlying mechanism, Zig makes three modules available to all programs: builtin, std and root.

I'm not sure I see the point of @import("root") TBH but, I like having the import mechanism be the same for std stuff and user stuff.

5

u/KattyTheEnby 8d ago

Zig makes three modules available to all programs: builtin, std and root.

  1. You don't need to explicitly define "root", do you?  (I know. You don't need to do it for std either, but it'd be worse UX if you had to.)
  2. builtin and std (typically) refer to the same thing, no matter what's being compiled, but root is different for each program. Isn't that right?

I feel like if the answers are "No." and "Yes." respectively, then I feel like that makes "root" a special enough case to warrant a specific function – and it would likely fit the language's goal to minimize implicit behaviors.

I'm not sure I see the point of @import("root") TBH

Apparently – if I understand correctly – it refers to whichever program is being compiled, allowing pulled in dependencies to see things about it, such as the declarations.

I looked it up and found this: https://ziggit.dev/t/why-use-import-root/6749/2

I couldn't find much official Zig documentation on it, other than a quick remark on the section for @import in the Zig Language Reference: “Alias for the root module. In typical project structures, this means it refers back to src/main.zig.”

5

u/chocapix 8d ago

I'm fine with @import("root") and there are already too many @functions IMO.

I looked it up and found this: https://ziggit.dev/t/why-use-import-root/6749/2

Oh yeah, good point. You need it if you're a library and want to look at std_options.

1

u/northrupthebandgeek 7d ago

I'm not sure I see the point of @import("root") TBH

One use case is to allow programs to optionally override library functions. For example, in some of the code I've written:

const root = @import("root");
// ...
/// N64 ROM entry point.  IPL3 calls this function after initializing
/// RDRAM and loading our code into it.
pub fn start() callconv(.c) noreturn {
    Debug.init();
    if (@hasDecl(root, "main"))
        root.main() catch @panic("main() returned an error")
    else
        @panic("no main(); nothing to do");
    @panic("main() returned");
}

comptime {
    @export(&start, .{
        .name = "__start",
        .linkage = .strong,
        .section = ".boot",
    });
}

9

u/TheKiller36_real 8d ago

well @import("no-dot-zig-or-zon") will always import a module - std and builtin are "really special" but root isn't, at least when you look at the "full" compile command line which includes something like -Mroot=src/root.zig just like for any other module; only special thing is that there has to be a root module, whereas -Mfoo=bar.zig is completely optional

2

u/torp_fan 3d ago

"I've only been using Zig for 5 minutes and haven't read the documentation but I already know better than the language designers and so I'm 'honestly' going to declare their decisions to be 'terrible' in a forum that they never read, so there's no point to this other than stroking my own ego."

@import("foo") where "foo" is a plain name, not a path, imports a module named "foo". The "root" module is defined in build.zig and usually points to src/main.zig

So yes, this is totally off-base.

3

u/Confused-Armpit 8d ago

The @import builtin already has some magic values (e.g. "std", "builtin", etc). They differ from importing regular files in the source tree is because they don't mention the extension (".zig"). So, unless you store source files without any file extension, it should be fine.

Although that idea does kinda make sense, along with @importStd and @importBuiltin, although it is kinda verbose.

11

u/UntitledRedditUser 8d ago

I'm not sure, but aren't builtin and std just considered modules, which just happen to always be available?

Edit: And then root is just the name of the current module.

5

u/ellopaupet 8d ago

AFAIK, this is correct

4

u/InKryption07 8d ago

root is the name of the root module, not the current one

1

u/UntitledRedditUser 5d ago

In zigs stdlib they often do stuff like: if (@hasDecl(root, "os") ...

Does that check if the stdlib has std.os, if I have made my own, or something else entirely?

1

u/InKryption07 5d ago

Like I said, it would check if the root module has it. The root module is the "user" compilation entrypoint, i.e. where your main function is.

4

u/SilvernClaws 8d ago
  1. The creators aren't reading here anyway, so it's not gonna do more than a philosophical discussion anyway.

  2. I feel like the builtin and root behavior might still change eventually. Using one basic built-in was probably the easiest way to implement the current approach.

  3. The documentation explains the magic values and the Zig Language Server suggests all available modules when typing an import, including these, so it's not too hard to find.

2

u/KattyTheEnby 8d ago

I second this, even though I am fairly new to Zig.

I understand Zig – or its ethos, at least – well, and I know it's all about being explicit and not hiding things.

While I get the argument from others, such as u/Confused-Armpit and u/chocapix, I think making it more clear when there is a special import, that is always available (as the Zig Language Reference says in its own words), is a better decision.

Also, I saw u/DokOktavo got downvoted because they committed the oh so horrible sin ov agreeing with the post author — downvoting comments on the basis ov opinions is an abuse ov the downvote functionality.

-1

u/DokOktavo 8d ago

I agree, I'd rather have @builtin(), @root() and @std() than @import("builtin"), @import("root") and @import("std").

5

u/actondev 8d ago

As things are you can use your own std by defining the module. So I find the current approach much better. std is yet another module which happens to be predefined for your convenience, but you're free to override (haven't tried but I'd guess it's possible right now)

1

u/KattyTheEnby 8d ago

std is yet another module which happens to be predefined for your convenience, but you're free to override (haven't tried but I'd guess it's possible right now)

But I'm guessing you almost certainly can't override root. If you can, that would be weird, since it is always supposed to be present, and is described as being a special item, making it important.

If we apply this logic (ov std) to the "root" argument in @import, then it makes sense, twofold, to have @importRoot() and @import("root") be different.*

(* @importRoot() always refers to the root module, whereas @import("root") would literally import a module which is, literally, called root.)

[CC: u/chocapix, u/Confused-Armpit, u/SilvernClaws, u/InKryption07]

1

u/DokOktavo 8d ago

Although you can set the std's path, it's not yet another module. There are types in std.builtin that affect various builtins.

0

u/zandr0id 8d ago

I like this. I'm in favor of using the same mechanism in as many places as possible, so I'd opt for even a step further and make it where you could opt to set up access to any module by the `@` syntax if it was manually linked in the build script, which the three core modules are.

3

u/InKryption07 8d ago

That would be ridiculous. Builtins are a static part of the AST, intended for compiler intrinsics.

Also, "manually linked in the build script" doesn't mean anything, there's no way to tell in the build graph if something was done "manually"