So I read the documentation for using
and import
in Julia. However what this does not tell me is how I should be using these two statements in practice (and, given the lack of orthogonality, this is not too easy).
Case in point: let's put the following trivial code in "myfile.jl"
:
module MyModule
f() = 1
export f
end
import .MyModule # or: using .MyModule
if I use import
on the last line, then f
is not exported to Main
namespace. However, when I change "myfile.jl"
(e.g. modifying the return value of f
) and then re-include
it, the function is replaced (this is the behaviour I wish for). (Note that I could explicitly import .MyModule: f
, but this introduces unneeded redundancy; also, the real-life case will involve a long list of functions with long names. OK, I might also write a macro that uses names(Main.MyModule)
, but I somehow feel that this ought to be simpler.)
if I replace import
by using
, then this is reversed: f
is now exported, but changing anything in the module now requires re-starting the Julia interpreter.
using both import
and using
exports only the first version of f()
to the main namespace: when I update the code, only the first return value is used.
So my question is not about the behaviour of both statements import
and using
, which are documented (if not explained) in the linked page, but about the intent behind these. Why two statements when one would be enough? Why does one of these ignore all export
directives? In which case am I supposed, in practice, to use each statement?
(version is 1.1.0. Also, this runs on a system without easy Pkg
access, so I did not try Revise
yet.)
My guess based on reading the docs: using is used to bring another module into the name-space of the current module. import is used to bring specific types/functions/variables from other modules into the name-space of the current module.
Julia uses git for organizing and controlling packages. By convention, all packages are stored in git repositories, with a ". jl" suffix. So the Calculus package is stored in a Git repository called Calculus.
Question: What is the difference between using and import in Julia when I'm building my own module? My guess based on reading the docs: using is used to bring another module into the name-space of the current module. import is used to bring specific types/functions/variables from other modules into the name-space of the current module.
Julia is using multiple dispatch and typically packages export functions that operate on typed arguments (those are however usually abstract types so functions functionality is not limited).
Typically, in larger Julia packages you will see module code organized into files, eg Files and file names are mostly unrelated to modules; modules are associated only with module expressions. One can have multiple files per module, and multiple modules per file. include behaves as if the contents of the source file were evaluated in its place.
We can avoid using exports by directly importing the types or functions that we want to use in Julia: A direct import will load the code we are targeting without including all of the other ingredients inside of the package.
The convention in Julia is to use using PackageName
unless you have some specific reason to do otherwise.
This language design decision is motivated simply by users' comfort. Julia is using multiple dispatch and typically packages export functions that operate on typed arguments (those are however usually abstract
types so functions functionality is not limited).
With this design all functions and methods implementing them can be in the same namespace since they differ on parameter types that are specific for their packages. Hence, a scenario where using
brings a package to the namespace that overrides existing method implementations are very rare (and then Julia reports a warning). This process is additionally controlled by package authors through using the export
keyword.
I think that it looks strange to you because you are accustomed to languages like Python. For an example numpy
has the exactly the same function names as the default functions and there is no good way to name them differently (e.g. try to think what would be a good alternative for a function named sin
:-) ). Hence, in Python you always need to import functions into their own namespaces perhaps with an alias such as import numpy as np
. After having several years of experience with both languages I would say that the way Julia does it is natural and the Pytonish way is strange :-)
Regarding import
and possibilities of bringing single functions from a package to your namespace, I see it as workarounds for situations where a name clash occurs. Also you need to explicitly do that if you want to add new method implementations to some other package (like defining a new meaning for the +
operator). Other than that you just type using
and never look back.
One thing to add, in addition to Przemyslaw Szufel's answer. When you are extending a method, it's possible to do that without using import
for the function that you are extending. For example, if I want to extend f
from the module Foo
, I can do using Foo
and then define a new method for Foo.f
. Here's a complete example:
module Foo
struct A
x::Int
end
f(a::A) = a.x + 1
export A, f
end
using .Foo
struct B
x::Int
end
Foo.f(b::B) = b.x + 2
The advantage of this approach is that when you are reading the code it is easier to see the origin of the function that you are extending.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With