Resolving a module by its name?

Given an arbitrary module name as a symbol, and an expression, what is the correct way to evaluate the expression in the context of the module?

For example, suppose I have the following tuple:

in = (ns = :MyModule, expr = :(x = 3 + 3))

Assume MyModule has been previously defined. Now I want to evaluate expr inside that module. The best I came up with is:

(@eval $(in.ns)).eval(in.expr)

Using @eval to convert a symbol to a module feels dirty, but I can’t find a getmodule function which could take a symbol and return a module. Am I missing anything?

Edit 1: Follow-up question. What is the right approach to dealing with nested submodules? Let’s say the ns field in the tuple is set to [:MyModule, :One, :Two, :Three], and the module in which I want to evaluate the expression is MyModule.One.Two.Three.

Edit 2: I came up with the following function which seems to do what I want:

function eval_module_expr(fully_qualified_module_name::Array{Symbol, 1}, expr::Expr)
   fqm = Main.eval(first(fully_qualified_module_name))
   for m in fully_qualified_module_name[2:end]
      fqm = fqm.eval(m)
   end
   return fqm.eval(expr)
end

Use as follows: eval_module_expr([:MyModule, :One, :Two, :Three], :(x = 3 + 5)). Is this a reasonable approach? It feels like I’m reinventing the wheel.

offtopic, but in is a function in julia, maybe another name?

I am not aware of a better way.

Can you give some context to your problem? It seems like you are doing something quite convoluted, (possibly for macros?). This is occasionally necessary but there may be a simpler solution.

1 Like

another way to do that

function eval_module_expr(modules::Array{Symbol, 1}, expr::Expr) 
   return Meta.parse(join(string.(modules),".") * "." * string(expr))
end

You can use getfield for modules:

julia> using ImageFiltering

julia> m = getfield(Main, :ImageFiltering)
ImageFiltering

julia> typeof(m)
Module

julia> m2 = getfield(m, :KernelFactors)
ImageFiltering.KernelFactors

julia> typeof(m2)
Module

One subtlety is that not all modules are known to Main, specifically, packages loaded to support other packages that you didn’t explictly import into Main. Base.loaded_modules has a list. You can also do this:

julia> using ImageFiltering

julia> ColorTypes
ERROR: UndefVarError: ColorTypes not defined

julia> m = Base.root_module(Base.__toplevel__, :ColorTypes)
ColorTypes

Finally, to evaluate your expression in a particular module you can use Core.eval(mod, expr).

In general, base/reflection.jl, base/loading.jl, and the Serialization stdlib are fantastic resources for these kinds of questions.

9 Likes

Thank you! This is exactly the right solution, and what I needed.

Could I ask for my own edification: what is the purpose of evaluating an expression in a module? What is your use case, for instance?

I’m hacking on a Julia interaction mode for Emacs in the spirit of Common Lisp’s SLIME and Clojure’s CIDER — both of which are superb, (pretty much) best-of-breed interactive programming environments, and which I miss when I use all other languages. Some features — like putting the current top-level form into the running REPL or system image — require evaluating Julia code in the context of the module where it is defined. These modules can be arbitrarily nested. Hence the need to resolve the module and eval things.

5 Likes

The Cider environment looks super powerful. (Except that my wrists hurt just hearing about all those control-meta-something-or-other key combinations.)

Thanks. Looking forward to trying this. I miss SLIME.

1 Like