Zig: usingnamespace error: import of 'someFunc' overrides existing definition

Created on 23 Aug 2020  路  4Comments  路  Source: ziglang/zig

i am trying to create for my projects some drop-in replacements/extensions for std packages, to extend or improve functionality of std library for my own use.

for that i tried to import the entire package namespace in my replacement package and override some of the definitions, then import the replacement package instead of the original.

for example:

// mymath.zig
const std = @import("std");
pub usingnamespace std.math;

// replacement fn with algorithm i like better
pub fn floorPowerOfTwo(comptime T: type, value: T) T {
    comptime assert(@typeInfo(T) == .Int and !T.is_signed);
    std.debug.assert(value != 0);
    return @as(T, 1) << log2_int(T, value);
}

unfortunately the above code fails with error: import of 'floorPowerOfTwo' overrides existing definition

i am not sure if this is a bug or design decision, but in my opinion local definitions should override imported ones, otherwise a local package could be broken at any time by introducing a conflicting name in an upstream package.

for example, if my function was named "floorPowerOfTwo2" to avoid conflict, a tomorrow's addition of "floorPowerOfTwo2" to std.math will break my previously working package.

please consider allowing local overrides to usingnamespace-imported names.

proposal

Most helpful comment

i am not sure if this is a bug or design decision, but in my opinion local definitions should override imported ones, otherwise a local package could be broken at any time by introducing a conflicting name in an upstream package.

It's a design decision that zig has absolutely no symbol shadowing. It's sometimes annoying, but in general it's pretty awesome. You can rely on your symbols being unique to the current file/scope and a name will always refer to the same element.

I am strongly against the implementation of such feature.

All 4 comments

i am not sure if this is a bug or design decision, but in my opinion local definitions should override imported ones, otherwise a local package could be broken at any time by introducing a conflicting name in an upstream package.

It's a design decision that zig has absolutely no symbol shadowing. It's sometimes annoying, but in general it's pretty awesome. You can rely on your symbols being unique to the current file/scope and a name will always refer to the same element.

I am strongly against the implementation of such feature.

i do agree against shadowing in general, mostly as a guideline but i could live with enforcing it.

as for symbols always representing the same entity, thats an overstatement - it really depends on the context. the symbol "a" imported from X is likely different than the same "a" imported from Y. so to really know what "a" represents, one has to be aware where it is imported from.

if you argue that the symbols are really X.a and Y.a, the same is true for symbols in my proposal - we have upstream.a and mylib.a - generally referring to the same entity, except where mylib.a is actually defined in mylib.

implementation is simple - "usingnamespace X" inserts in local namespace all public symbols from X not defined locally.

for usage there is no confusion - one can import "upstream" or import "mylib" - its visible in the source.

this is a simple, non-intrusive way to locally add functionality to the library and fix bugs. which is even more important in the context of platform-under-development, what is zig currently.

@npaskov I think the problem this tries to avoid is not with regards to a single state, but to a history of changes.

  • Say you have a function in mymath.zig which uses a symbol via usingnamespace std.math. It requires no prefix, so just f.e. const x = atan(angle); in code.
  • Somebody wants to add an implementation of atan to your mymath.zig, not realizing that

    • (A) std.math already provides this function,

    • and more importantly (B) code was already relying on the previous implementation and its interface.

      (With math functions their interfaces are often standardized, but even here they might still differ in accepted bounds, caller/callee checking error conditions, etc.)

With your proposed semantics, the change passes and silently changes which function the rest of the code is referring to.

In status quo, the compiler complains, causing you to realize (A), and hopefully consider the possibility of (B) before making the change. (F.e. by splitting it into one commit removing the usingnamespace - spotting the error - and a second one adding the custom atan function.)

(I also assume incremental compilation (and linking?) loses a source of errors when dependencies cannot shadow each other, though that might not be enough reason on its own.)

how is this different from somebody changing the function in std.math? then users of std.math will "silently" use different code without knowing.

users of mymath.atan dont expect it to be the same as std.math.atan, otherwise they'd use std.math.atan. they expect mymath.atan to provide the same contract, or its superset. it is the author's responsibility to maintain that contract, be it in std.math or mymath.

in my proposal the whole point of having mymath with "pub usingnamespace std.math" is to provide a "replacement" alternative to std.math with fixes/optimizations/extensions. using one or the other is explicit choice of the code developer, so there are no misplaced expectations.

currently the only non-intrusive way to achieve this "replacement" effect is to manually import every single item from the std package, which could be hundreds of symbols (or thousands if upstream is like os.windows). i'm sure a similar kind of issues led to the introduction of "usingnamespace" in the first place.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

andrewrk picture andrewrk  路  3Comments

zimmi picture zimmi  路  3Comments

andrewrk picture andrewrk  路  3Comments

fengb picture fengb  路  3Comments

jorangreef picture jorangreef  路  3Comments