When to use `using Package: function` and when to use `import Package: function`

The reason why import Blah: foo is usually discouraged is that forcing qualification gives you added protection, albeit at runtime, from silently mixing up two same names intended to be from different modules. Consider one scenario where you start writing some other included file down the line and you forgot you imported foo way back, so you start making a bunch of methods for what you think is a new function foo to do something else. At runtime, those methods are added to the imported function foo instead, easily breaking Blah by replacing methods or affecting dispatch.

Of course the reasonable counterargument is that you should keep all your imports in one place and check the names there regularly to avoid accidental name reuse. After all, variable accesses and function calls aren’t often qualified, so you’ll have to check the list anyway to distinguish the module’s names from the imports. This incidentally is why I never bought the other argument that qualification is important for distinguishing imports at first glance; if it were so important we’d be qualifying everything like in Python.

Now consider the reverse scenario where you decide to import a new package’s function import Blah: foo at the start but you forgot you had already made an internal function foo somewhere in a buried included file. Now you’ll have to check ALL names in ALL files to avoid name reuse. A linter could do this automatically, but it’s nice if the base language also warns or stops you at runtime before anything starts running for days. This runtime protection is already there for reassignment, even for import, so it’s nice if that were consistent for adding methods:

julia> module A; x = 1; foo() = 0 end
Main.A

julia> import .A: x, foo

julia> x = 2
ERROR: cannot assign a value to imported variable Main.x
Stacktrace:
 [1] top-level scope
   @ REPL[3]:1

julia> A.x = 2 # reassignment needs qualifying
2

julia> foo(x) = 1 # adding method does not need qualifying
foo (generic function with 2 methods)

That’s not to say import Blah: foo should never be used. A small enough module won’t span enough files to run into such issues, and base Julia does import Base: ____ a lot because those symbols are more readily recognized anyway. Macros don’t have many argument types worth dispatching over, but when they do, module qualification currently does not work so you need import Blah: @foo. That’s not so bad though because extending macros from other packages is type piracy and should be avoided.

5 Likes