Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Julia - Metaprogramming for using several modules

I'm using Julia to autograde students' work. I have all of their files Student1.jl, Student2.jl, etc. as separate modules Student1, Student2, etc in a directory that is part of LOAD_PATH. What I want to be able to do works completely fine in the REPL, but fails in a file.

macro Student(number)
    return Meta.parse("Main.Student$number")
end

using Student1
@Student(1).call_function(inputs)

works completely fine in the REPL. However, since I'm running this in a script, I need to be able to include the modules with more metaprogramming that is currently not working. I would have thought that the exact same script above would have worked in a file Autograder.jl by calling

@eval(using Student1)
@Student(1).call_function(inputs)

in a module Autograder. But I get either an UndefVarError: Student1 not defined or LoadError: cannot replace module Student1 during compilation depending on how I tweak things.

Is there something small in Julia metaprogramming I'm missing here to make this autograding system work out? Thanks for any advice.

like image 338
NoseKnowsAll Avatar asked Apr 25 '26 15:04

NoseKnowsAll


1 Answers

The code just as you have written works for me on julia versions 1.1.0, 1.3.1, 1.5.1, 1.6.0 and 1.7.0. By that I mean, if I add an inputs variable and put your first code block in a file Autograder.jl and run JULIA_LOAD_PATH="modules:$JULIA_LOAD_PATH" julia Autograder.jl with the student modules in the modules directory I get the output of the call_function function in the Student1 module.

However if Autograder.jl actually contains a module then the Student$number module is not required into Main and your macro needs to be modified accordingly:

module Autograder
macro Student(number)
    return Meta.parse("Student$number") # or "Autograder.Student$number"
end

inputs = []
@eval(using Student1)
@Student(1).call_function(inputs)
end

Personally I wouldn't use a macro to accomplish this, here is one possible alternative:

student(id) = Base.require(Main, Symbol("Student$(id)"))
let student_module = student(1)
    student_module.call_function(inputs)
end

or without modifying the LOAD_PATH:

student(id) = include("modules/Student$(id).jl")
let student_module = student(1)
    student_module.call_function(inputs)
end
like image 96
ahnlabb Avatar answered Apr 27 '26 09:04

ahnlabb