Quiz 00.1 on Modules, packages and environments
Q1: What is printed ?
Given a file foo.jl
with the following code:
println("A - I am in foo.jl")
module Foo
println("B - I am in module Foo in foo.jl")
export x
const x=1
const y=2
z() = println("C - I am in a function of module Foo")
end
module Foo2
println("D - I am in module Foo2 in foo.jl")
export a
const a=1
const b=2
c() = println("E - I am in a function of module Foo2")
end
Which print statements will appear after running the command include("foo.jl")
?
RESOLUTION
Including the file would result in evaluating the code in the file and hence in the statements A
, B
and D
to be printed. Statements C
and E
are within function definition and would occur only when functions z
or c
would have been called.
The correct answer is:
- "Statements
A
,B
andD
only"
Q2: Inclusion of a module
Given a file foo.jl
with the following code:
module Foo
export x
const x=1
const y=2
z() = println("Hello world!")
end
and the following sequence of commands:
include("foo.jl") # Command 1
x # Command 2
Foo.x # Command 3
using Foo # Command 4
using .Foo # Command 5
x # Command 6
Foo.z() # Command 7
Which statements are correct ?
RESOLUTION
The include("foo.jl")
statement evaluates the included content, but it doesn't yet bring it into scope. You can't yet refer directly to the objects of the Foo
module, you need to use the qualified name as in command 3. Foo
is a module, not a package, so command 4 will complain that it doesn't find the "package" Foo
. After the module has been bring to scope we can refer to x
directly as in command 6. Command 7, as we are using the qualified name, is indipenden than whether z
was exported by Foo
or not, and hence it works, and would have been worked even without the using .Foo
of command 5.
The correct answers are:
- "Command 3 returns the value
1
" - "Command 4 returns an
ArgumentError: Package Foo not found in current path:
" - "Command 6 returns the value
1
"
Q3: Submodules
Given a file Foo.jl
with the following code:
module Foo
export x, plusOne
x = 1
plusOne(x) = x + 1
module Foo2
export plusTwo
plusTwo(x) = plusOne(x)+1
end
end
After including the file we try to run the command Foo.Foo2.plusTwo(10)
. Which of the following statements is correct ?
RESOLUTION
The given command results in a UndefVarError: plusOne not defined
. Indeed even if Foo2
is a submodule of Foo
, it doesn't inherit the scope of parent modules. So its code can't find the 'plusOne' function. When in the REPL we run the command we are in the Main
module. Adding using .Foo
doesn't change anything, as the problem is in the scope of the Foo2
module, not in those of the REPL (Main
- and , of course, typing using Foo
looks-up for the package Foo
, not the module Foo
, and would end in a Package Foo not found
error. So what can we do? One solution is using in the plusTwo
function the full path of the plusOne
function: plusTwo(x) = Main.Foo.plusOne(x)+1
. While this works, it may be a less portable solution, as it then requires module Foo to be a child of Main
. Perhaps a better solution is to use a relative path and use the statement using ..Foo
in module Foo2
before the definition of plusTwo
(trying to use a relative path directly in the function definition as in plusTwo(x) = ..Foo.plusOne(x)+1
would result in a parsing error)
The correct answers are:
- The result is an error that we can avoid if the function
plusTwo
in moduleFoo2
is defined asplusTwo(x) = Main.Foo.plusOne(x)+1
- The result is an error that we can avoid if in module
Foo2
the functionplusTwo
is preceded by the statementusing ..Foo
Q4: Submodules2
Given a module Foo
with the following code:
module Foo
export x
x = 1
module Foo2
export plusTwo
plusTwo(x) = x+2
end
module Foo3
export plusThree
[XXXX]
plusThree(x) = plusTwo(x)+1
end
end
Which of the following statements are correct ?
RESOLUTION
The function plusTwo
needs to access a function on a sibling module. So the module Foo2
must be retrieved by going up to one level with the two dots and then naming the module, i.e. using ..Foo2
or using the full module path using Main.Foo.Foo2
. import
statemens alone will not work as the plusThree
function call the plusTwo
function using the unqualified name, without prefixing the module, so the plusThree
function name need to be exported.
The correct answers are:
- "
[XXXX]
should beusing Main.Foo.Foo2
for the functionplusThree
to work" - "
[XXXX]
should beusing ..Foo2
for the functionplusThree
to work"
Q5: Reproducibility
Which elements do you have to provide to others to guarantee reproducibility of your results obtained with a Julia project?
RESOLUTION
To provide replicable results, assuming a deterministic algorithm or one where the random seed generator has been fixed, we need to provide the input data, the source code and the 'Manifest.toml' file that describe the exact version of all packages. The Project.toml
file instead, when present, is used to describe in which conditions our scripts could be used (i.e. the list and eventually range of dependent packages), but not a unique environment state. The information of the Manifest.toml
(and, for Julia versions before 1.7, the Julia version itself, as this info was not encoded in the Manifest.toml
file) is enougth, we don't need to provide the whole content of the user Julia folder.
The correct answers are:
- "The input data of your analysis"
- "The full source code of the scripts you have used"
- "The file
Manifest.toml
of the environment where your code ran to produce the results"