Difference between "using x" vs "using .x" or "using Main.x"

Got curious about this behaviour:

] add  DataFrames     # if needed

using .DataFrames     # not ok
using Main.DataFrames # not ok
using DataFrames      # ok
using .DataFrames     # now ok
using Main.DataFrames # now ok
module foo
end
using  .foo           # ok
using Main.foo        # ok
using foo             # NOT ok

Let me see if I got it… using when it is followed with either Main.x or .x looks for a module already loaded and bring its objects into scope, otherwise it does a completely different thing: it expect a package, and use the package metadata to lookup the correct version of module x in the package, it loads it, and it bring its objects to scope, right ?

Edit:
If so… what import .x (or import Main.x) does ? nothing (either the module is already loaded and it does nothing or it is not imported but it can’t be retrieved) ?

1 Like

I find that a little bit confusing as well. You can think like a directory structure. The packages and Main are at the top level of that structure. For the modules which are at the top level, you do not use any dot:

juila> using Main

julia> using Pkg

But when you are inside a module (we are "inside the module Main" at the REPL - correct me if this image is wrong), we can refer to the modules in this module using . (as to the files in the current directory):

julia> module Foo
         x = 1
         export x
       end
Main.Foo

Up to this point is like if Foo was a subdirectory of Main, and thus you have to refer to things within it with:

julia> Foo.x
1

julia> Main.Foo.x
1

When you do this, however:

julia> using .Foo

You bring Foo to the same level as Main.

You can use that “directory image” for understanding nested modules, for example:

julia> module Foo1
         module Faa1
           x = 1 ; export x
         end
         module Foo2
            module Faa2
              y = 1 ; export y
            end
            module Foo3
               module Faa3
                 z = 1 ; export z
               end
               using .Faa3
               using ..Faa2
               using ...Faa1
               f() = x + y + z
            end 
          end
       end
WARNING: replacing module Foo1.
Main.Foo1

julia> Foo1.Foo2.Foo3.f()
3


1 Like

using X and import X bring the module X into scope. (And using also brings its exported objects). Obviously, X must be a package recognized by your environment (say listed in the current Project.toml).

On the other hand, when you have defined X in Main (e.g. typing it in the REPL, as in your example), X is not recognized by your environment, so you have to tell the package manager that it is Main.X (or .X for short).

Never tried it, but import .X might be used to bring specific methods for extension into scope, e.g. import .X: foo.

You can do that with using as well:

julia> module Foo
         x = 1
         y = 2
         export x, y
       end
Main.Foo

julia> using .Foo:x

julia> x
1

julia> y
ERROR: UndefVarError: y not defined