Why allow cyclic imports?

Hi! I wonder why Zig doesn’t prevent circular imports. Wouldn’t such a restriction speed up builds and make the build system simpler just like it does in Go?

2 Likes

Wouldn’t that break everywhere?
E.g. just take a random file from the standard library, and you will notice that the first line is something like const std = @import("../std.zig");, which is creating a circle.

Well yes, if they were to change it now, it might break a lot of stuff, but that doesn’t explain why they allowed it in the first place.

I’m going to take a wild (uneducated) stab in the dark and guess it has to do with C compatibility.

Avoiding cyclic dependencies would probably be difficult because there is usually no separation between interface and implementation in Zig.

1 Like

Not an answer to why per-se, but I like that there are circular imports, It means i can group things how i think is natural, without having to add extra files just to satisfy the compiler. Note that circular dependencies are not allowed, for obvious reasons.

I compare it with Python. In python an import of a module is an inherent dependency on everything in that module. In zig an import is just a handle that allows you to namespace or use specific items from that file, without depending on everything else in that file.

4 Likes

My question is, why would you want to restrict circular imports?

  • Build speed? Zig builds fairly fast, and this wont make much of a difference either way. The main slow down is LLVM, which is being addressed right now. (custom backend with incremental compilation)
  • For code clarity? I don’t think its a problem, its fairly intuitive especially with the struct base of imports.
  • Also a real thing is developer experience, and not needing to worry about import order is really nice.
  • Zig is also lazily analized, so it is not making any huge restrictions on the build system itself (to my knowledge)
3 Likes

no, zig evaluates everything lazily, only the first used import of a file that zig sees triggers evaluation, though only for what is being used, latter uses will be a lookup unless it’s something that hasn’t been used before.

internally sure. It wouldn’t make using it simpler, and it would make code structure more complicated

3 Likes

In the far past I was using Delphi (later moved to C# and Zig).
In Delphi you somwtimes have to code things in a strange way to prevent circular references.
Not having ‘circular’ references is a blessing in my opinion. Not everything is a tree.

I would rather ask, why not allow cyclic imports? I would assume that Zig keeps track of already imported modules, and doesn’t import another time when it encounters the same import, which automatically breaks any cycles - also the compiler only picks the stuff from imported modules that’s actually used, so an import is probably more like populating a ‘table of contents’ instead of doing actual compilation work.

Keeping track of imports to check for cyclic imports (just to throw an error) sounds like it’s at least the same effort for no additional gain (since the redundant imports are dropped anyway - assuming that the Zig import system works like I think it does).

1 Like

When modules are separately compiled there is a chicken-egg problem because the first module in compilation order depends on others that are not build yet.
Zig compiles everything as a root module and there is no cyclic import problem.

2 Likes