On Thu, 22 Dec 2016 10:17:33 +0000, Joakim wrote: > Opening a file or 10 is extremely cheap compared to all the other costs > of the compiler. Purely from a technical cost perspective, > I'm not sure even scoped imports were worth it, as my simple > investigation below suggests.
The compiler doesn't merely have to open the file. It has to run at least partial semantic analysis on it in order to locate symbols. When templates are involved, this can become quite expensive. With static and selective imports, this step can be entirely avoided -- the current file tells you which files contain which declarations. With either, the compiler can tell exactly which file contains which declarations, so it can avoid pulling in anything beyond what's strictly necessary. With declaration-scoped imports, you avoid the same step. However, it requires the maintainer's discipline to reduce the declaration's imports to the minimal possible set of imports. For instance, I have a declaration: import myapp.users, std.socket; bool isUserOnline(User user, Socket userSocket); I decide that this needs compilation time optimizations. The current way: static import myapp.users, std.socket; bool isUserOnline(myapp.users.User user, std.socket.Socket socket); And Andrei's way: bool isUserOnline(User user, Socket userSocket) import myapp.users, std.socket; I refactor things so that this check finds the user socket on its own. The current way: static import myapp.users, std.socket; bool isUserOnline(myapp.users.User user); Eh, I forgot I don't need std.socket anymore, but this costs a few microseconds of compiler time to add it to the current symbol table. It has to allocate a lazily expanded module stub. Shouldn't be a huge deal. Andrei's way: bool isUserOnline(User user) import myapp.users, std.socket; Again, I forgot to update the imports, but this time the compiler has to read std.socket from disk, parse it, run semantic on it, and import all its top-level symbols into the scope's symbol table. Because there's nothing here that says the 'User' type is in myapp.users instead of std.socket. --- In point of fact, selective and static imports should be *faster* than Andrei's way. Consider: static import myapp.users, std.socket; bool isUserOnline(myapp.users.User user, std.socket.Socket socket); This has to locate a declaration named `User` in myapp.users, and it has to locate a declaration named `Socket` in std.socket. But let's look at Andrei's way: bool isUserOnline(User user, Socket userSocket) import myapp.users, std.socket; Here, the compiler has to search *both* myapp.users and std.socket for a declaration named `User`, then it has to search both for `Socket`. (Even if it finds `User` in the first, it still needs to search the second in case both define that symbol.) You go from O(distinct number of types referenced) lookups to O(types * imports). Granted, you'll usually have between one and three types, between one and three imports, so the point is a bit less salient.