Removal of workspace() and a way to clear variables


#1

The removal of workspace() is a major change that seems to have been done without much discussion (Rationale for the removal of workspace()?, 25046, 10351, 22434). The suggested replacement, Revise.jl, might be good for module developers and such use cases, but it’s not at all convenient for the type of exploratory coding and data analysis that’s common in scientific computing.

Often at the beginning of a project, I use the REPL to play around with the data and try out transformations, take different paths and circle in to the actual code to be written. Once I have a mental picture of the code, then I transfer that to a program file and then continue on with debugging, optimization, etc. Being able to clear the workspace between different stages of this is really convenient to make sure old variables and function definitions don’t interfere in the current stage of exploration. Things like Matlab’s clear all and the old workspace() are quite useful for this.

Moving this to an external module itself seems like a step backward, but Revise not even a full replacement. It might be theoretically possible to use Revise.jl for this kind of workflow using a scratch .jl file and tracking that (at least when/if Revise is able to deal with lambdas, macros, outdated method definitions, etc.), but that’s similar to saying we don’t need an REPL because we can create scratch files and run those with Julia - the difference in speed and convenience (and not having to switch contexts to an editor) make a qualitative difference in practice.

Is it possible to bring workspace() back? Or, if workspace() itself is too much of a pain to implement, can we have a clear(...) type function/macro to clear particular method definitions, constants, variable bindings (this would be a convenience over for eg. setting the variable to nothing), etc. by specifying them by name?


#2

I take issue with this. I have been using Revise.jl for just this sort of work for a few months now, and I have been very happy with it. It might require tweaking your approach if you are not used to keeping most of your code in modules (this was easy for me because I usually did this anyway), but there is very little in my day-to-day work that Revise.jl does not accommodate. Frankly the only times I wind up reloading is when some functions in my module were originally defined through some sort of code generation.

As far as lambdas go, I find that in practice most of these are defined within functions which Revise replaces just fine. So I almost never have a problem with this. Macros and code generation definitely do present a difficulty, but at least for me this is a relatively small part of my daily workload.

I definitely sympathize with being uncomfortable that there are “obsolete” references floating around one’s namespace, but at least for me this discomfort is more due to paranoia than any real problem. Besides that, workspace did not allow the type of real-time editing of modules that Revise does, which is a wonderful feature that is hard to find even in interpreted languages. My plea to people who find the removal of workspace to be a step backward is to work with Revise for a while and get used to it a bit before deciding that it is a problem. Again, it might require you to tweak your approach depending on how you are used to doing things, but as someone who has been using it very extensively for a few months I can testify that it really is quite a wonderful approach to the “screwing around” that I need to spend so much of my working life doing.

(Having said all that, I would also welcome a way of clearing non-module, non-const variables in Main, but I don’t consider it a particularly high priority.)


#3

The way workspace worked was fairly broken with the new way that packages are loaded. We may be able to replace it with something that’s less broken but adding that functionality hasn’t been a high priority.


#4

My understanding is that when you type x=1 in the REPL, it adds an x binding in the Main module, it’s a bit like typing:

module Main
    x = 1
end

It seems Julia keeps some sort of list of all the bindings in a module, is there a reason why we can’t remove an element from that table ? e.g. delete!(Main,:x).

Otherwise maybe something like REPLinModule might a workaround, to get a clean workspace you would do module Testing end, REPLinModule.set(Testing) and your REPL would then be evaluating in an empty module (which is more or less what workspace does, as I understand).


#5

Yes: the compiler generates code that assumes those entries exist.

But I agree it should be possible to create a new module and “attach” the REPL to it. That seems like the right functionality for this.

Actually I think it’s more the opposite: the old way of loading packages by referencing them from Main conflicts much more with the idea of changing the Main module. Now that the notions of “which packages are loaded” and “what bindings are in my workspace” are de-coupled, replacing Main or pointing the REPL to another module should be easier.


#6

My code too usually ends up in modules (depending on project complexity), but what I’m talking about is much earlier in the process when you don’t know what functions you want, much less where module divides lay, while the solution is still taking shape.

Yeah, the discomfort is an added mental tax, but it’s also been a real (if minor) problem to me in different ways, method definition issues with functions and unintentional variable use (similar to coding in Perl pre-use strict).

FWIW, I’ve also been using Revise for a few months now (though probably not as extensively), and tried to give it a real try, but I still find it inconvenient and a productivity barrier.


#7

Why is that any different? Nobody is forcing you to stay committed to anything, just make a module somewhere, put some functions in it, go nuts! Granted, depending on what you are doing your namespace may be littered with some old functions, but I’ve never found that to be much of a problem. (By the way, it seems like it would be relatively simple to edit Revise such that if you call deleted functions it throws a MethodError… not quite sure why they haven’t done something like that.)

Could you be a little more specific? I use it pretty much all day every day, and the only pain point for me is macros and code generation. I might be able to offer some useful advice.


#8

We’re in agreement here. However, it might be better to call this function clear() and to define clear(:name) to do clear() to get a fresh main module and then create bindings for everything that was in the old main except for name.


#9

Methods can be deleted in 0.7:

And https://github.com/timholy/Revise.jl#method-deletion


#10

Maybe a dumb question: do MethodInstace and consequently Methods, get garbage collected when
there is no longer any references to them?

Or in other words, is it possible to remove types from the type tree dynamically?


#11

I tried something like this:

A = baremodule AA
    abstract type Foo end
    (::Type{Foo})(x,y) = 5
    #make sure a methodinstance exists
    bar = Foo(1,2)    
end

A=[]

It complains that module is not top level … meaning that Modules are treated differently , and probably are considered constant and their lifetime is considered infinite once they are loaded.
So it would be impossible to safely remove a type from the type tree and consequently impossible to
delete methods…

Is this correct?


#12

You can at least “hide” them https://github.com/JuliaLang/julia/pull/25050


#13

Sorry for the slow reply here, “real life” got pretty compelling for a while and I’ve not been able to keep up or even notice all the relevant forms of communication.

You’re definitely right about some of Revise’s limitations…especially on 0.6. The master branch of Revise has been 0.7-only since Oct 2017 (presumably one of the earliest packages to do that), so 0.6 has gotten no love whatsoever for about 10 months now. If you’ve not tried Revise on 0.7 or 1.0, you’ll find there are many changes (hopefully, improvements)—the 0.7+ version of Revise has nearly 3x the number of commits that the version on 0.6 has (272 vs 100). I’ll document some of those changes elsewhere, but for the concerns you described probably the most noteworthy addition is method deletion.

Revise aims to help people’s workflow, so if things still aren’t great for you on 0.7, by all means file issues that demonstrate points of pain. Fixing your pain-points might be easy or might be (currently) impossible, but in any event please don’t assume I already know about your concerns—for me, the last few weeks have been a real eye-opener of just how differently Revise behaved on some people’s machines than on mine. Many of my commits have been focused on trying to make sure it runs better on a wider diversity of machines and handles a wider array of use-cases. A lot of progress has been made, but I definitely don’t think we’re done yet, so when you encounter frustrations please do report them.