`using SomePackage` in block

How can I call using SomePackage to load a package inside a block? Be it begin, a function, or any other block.

Here is my attempts at loading GLMakie in a block:

julia> begin
           using GLMakie
           L"f(x)"
       end
ERROR: LoadError: UndefVarError: @L_str not defined
in expression starting at REPL[2]:3

But the same code works when the lines are run one at a time.

julia> using GLMakie

julia> L"f(x)"
L"$f(x)$"

I would also like to load Pkg as part of some sysimage generating function to programmatically update packages.

You cannot load a package within a function, begin, let, etc scope. Packages can only be loaded at “top level”. This means the top-level of a module scope or in the REPL (which is actually a module called Main). I am not aware of any additional places, but perhaps someone else can list more.

Loading a package is potentially side-effectful. It changes the method tables for any functions it extends. I suspect this is why it cannot be done within a narrow scope. For that matter, loading a module anywhere still affects those same extended functions in all scopes.

Why is it necessary for you to load a package within a narrow scope? Perhaps we can suggest a reorganization that will help alleviate whatever issue you perceive. Are you primarily concerned with namespaces?

EDIT: I was mistaken. You can load a module within a begin or let block (but not a function). But it will not contain the module to just that block. It will be as if you had loaded it outside the block.

julia> SVector(1,2,3)
ERROR: UndefVarError: SVector not defined
Stacktrace:
 [1] top-level scope
   @ REPL[25]:1

julia> begin; using StaticArrays; SVector(1,2,3); end
3-element SVector{3, Int64} with indices SOneTo(3):
 1
 2
 3

julia> SVector(1,2,3) # StaticArrays loaded within the entire module
3-element SVector{3, Int64} with indices SOneTo(3):
 1
 2
 3

You can load a package in a block without any issues, but not in a function. Your example just happens to use a macro from the package in the same block it was imported: this is not possible. A block is parsed as a single unit, and macros have to be expanded beforehand.
Functions from loaded packages can be used immediately, for example.

3 Likes

The problem is that in a regular block, julia is macroexpanding the whole thing at once, so it tries to expand the macro before it gets loaded.

One way around this would be to use this:

macro toplevel(ex::Expr)
    if ex.head == :block
        ex.head = :toplevel
        return esc(ex)
    else
        throw(ArgumentError("@toplevel must be used on `begin ... end` blocks"))
    end
end

Now, writing

@toplevel begin
    using GLMakie
    L"f(x)"
end

should work.

1 Like