Skip to content

module M shouldn't be bound to a name in the enclosing module when the definition fails #51828

@nsajko

Description

@nsajko

module ModName ... end does two things:

  1. Creates the module ModName

  2. 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:

  1. It clutters the name space of the enclosing module unnecessarily

  2. 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 and b. When an error happens after a is defined, but before b is defined, and if a depends on b, 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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions