-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Description
module ModName ... end
does two things:
-
Creates the module
ModName
-
Binds the name
ModName
in the enclosing module to the value of the newly defined module
Currently, the latter happens even when the first step throws. Two small examples:
julia> module A
error("e")
end
ERROR: e
Stacktrace:
[1] error(s::String)
@ Base ./error.jl:35
[2] top-level scope
@ REPL[1]:2
julia> isdefined(Main, :A)
true
julia> module B
module C
error("err")
end
end
ERROR: err
Stacktrace:
[1] error(s::String)
@ Base ./error.jl:35
[2] top-level scope
@ REPL[3]:3
julia> isdefined(Main, :B)
true
julia> isdefined(B, :C)
true
This problematic for two reasons:
-
It clutters the name space of the enclosing module unnecessarily
-
It seems unsafe, in a way. If defining a module throws/errors at all, in practice this indicates user error, and it also almost surely means that the new module is not defined in its entirety, as intended. Suppose the module was intended to define, for example, two names
a
andb
. When an error happens aftera
is defined, but beforeb
is defined, and ifa
depends onb
, the module is internally inconsistent, so it may be misleading to bind its value to a name in the enclosing module. Example:
julia> module M
a() = b()
error("e")
b() = 3
end
ERROR: e
Stacktrace:
[1] error(s::String)
@ Base ./error.jl:35
[2] top-level scope
@ REPL[1]:3
julia> M.a()
ERROR: UndefVarError: `b` not defined
Stacktrace:
[1] a()
@ Main.M ./REPL[1]:2
[2] top-level scope
@ REPL[2]:1
The latter point seems somewhat related to the atomicity in database transaction terminology. In one book on databases, it's defined as:
The “all or nothing” property. A transaction is an indivisible unit that is either performed in its entirety or is not performed at all.
Would it be possible to avoid binding the new module to a name in the enclosing module when defining the new module fails?