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