Using the new "Contextual Module REPL" to clear the workspace

I was very interested to see the Contextual module REPL in the Julia 1.9 Highlights.

People in the past have asked about how to clear the REPL workspace, e.g.,

It seems like the contextual module REPL goes a long way to enable this. I’d be inclined to put the following function in my startup.jl:

function temprepl()
    name = gensym("REPL")
    @eval module $name; import REPL; deactivate() = REPL.activate() end
    @eval REPL.activate($name)
end

That way, temprepl() activates a temporary workspace which can be “deleted” (caveat below) by running deactivate(). A use case for this would be including some test file or to do some prototyping in the REPL without having to worry about polluting the workspace.

This still requires some forethought, as there’s no way to get an actual new Main this way. Also, I don’t think the temporary workspace module would be garbage collected (it remains as a submodule of Main, as far as I understand).

Is this temprepl a good idea, or an abuse of what the contextual module REPL is intended for? Any ideas for improvement? There’s probably no way to actually delete (garbage-collect) the temporary module after it is deactivated, right?

4 Likes

Is this temprepl a good idea, or an abuse of what the contextual module REPL is intended for?

FWIW, when I was using a branch with this feature some time ago, I did something very similar, and I wouldn’t say this is abuse, if it’s useful to you!

For example, here is what I have as part of my startup script:

const LAST_MODULE = Ref(Main)
const MAINS = Module[]
const ASSIGNED_MODULES = fill(Main, 9)

function activate_module(s::LineEdit.MIState, mod::Union{Nothing, Module}=nothing)
    last_module = Base.active_module()
    if mod === nothing
        # parse module name in REPL input
        LineEdit.activate_module(s)
    else
        REPL.activate(mod)
        LineEdit.refresh_line(s)
    end
    if last_module != Base.active_module()
        LAST_MODULE[] = last_module
        true
    else
        false
    end
end

function new_Main(mod::Symbol)
    newmod = @eval Base.__toplevel__ module $mod
    # whatever you like to have in your REPL
    using BenchmarkTools
    # ...
    end
    newmod
end

function get_Main(nn)
    modname = Symbol("Main", nn)
    for ii = length(MAINS)+1:nn
        push!(MAINS, new_Main(Symbol("Main", ii)))
    end
    MAINS[nn]
end

activate_Main(s, ii) = activate_module(s, get_Main(ii))
activate_new_Main(s) = activate_module(s, get_Main(length(MAINS)+1))

assign_module(s, ii) = ASSIGNED_MODULES[ii] = Base.active_module()
activate_module(s, ii::Int) = activate_module(s, ASSIGNED_MODULES[ii])

And then in my REPL shortcuts:

    "\emn" => (s, o...) -> activate_new_Main(s),
    "\emm" => (s, o...) -> activate_module(s),
    "\em\em" => (s, o...) -> activate_module(s, LAST_MODULE[]),
    "\em0" => (s, o...) -> activate_module(s, Main),
    "\em1" => (s, o...) -> activate_Main(s, 1),
    "\em2" => (s, o...) -> activate_Main(s, 2),
    "\em3" => (s, o...) -> activate_Main(s, 3),
    "\em4" => (s, o...) -> activate_Main(s, 4),
    "\em\e1" => (s, o...) -> activate_module(s, 1),
    "\em\e2" => (s, o...) -> activate_module(s, 2),
    "\em\e3" => (s, o...) -> activate_module(s, 3),
    "\em\e4" => (s, o...) -> activate_module(s, 4),
    "\ems1" => (s, o...) -> assign_module(s, 1),
    "\ems2" => (s, o...) -> assign_module(s, 2),
    "\ems3" => (s, o...) -> assign_module(s, 3),
    "\ems4" => (s, o...) -> assign_module(s, 4),

There’s probably no way to actually delete (garbage-collect) the temporary module after it is deactivated, right?

Right, but you could go over its bindings and try assigning nothing to them.

1 Like