# \[ANN\] Revise 3.0: faster and better

**URL:** <https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287>\
**Category:** Package Announcements\
**Tags:** revise\
**Created:** [September 8, 2020, 7:30pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287 "2020-09-08T19:30:34Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [September 8, 2020, 7:30pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/1 "2020-09-08T19:30:34Z")

</div>

I’m pleased to announce that Revise 3.0 is available ~~for beta-testing~~ on Julia 1.0, 1.5, and nightly. Switch to it by updating your packages. (EDIT: this was originally written when Revise 3 was in beta but it is now released, and the changes are so minor it is not worth a new announcement.)

Revise 3 is an extensive rework with many exciting new features. ~~It also has one area where an important new configuration might need to be tweaked via user feedback. Since that setting won’t change once the official Revise 3.0 is out, by beta testing you have a chance to affect the next stage of of the Revise experience.~~ Read on for the full details.

### Faster startup

The biggest negative of Revise 2.x is that it initially slows you down: on my machine, the aggregate overhead of using Revise (startup + first subsequent package load) is about 3.5s. With Revise 3, on my machine that time is down to 0.9s; on nightly (what will become Julia 1.6), it may get as low as 0.7s depending on the fate of a still-to-be-decided pull request to Julia.

Most of these gains have been achieved by _deferring_ compilation, not making compilation faster or reducing the need for compilation. Basically, Revise now does much less work when it starts up, and goes to pains to ensure that it doesn’t trigger very much compilation until its functionality is actually needed. Consequently, this is mostly a bookkeeping improvement: time that used to be spent waiting for Revise to start will now be spent as extra latency for your first actual revision. To me, that still seems like a big win, because now Revise costs you very little until the moment when it’s actually delivering value. As before, it should be fast after the first revision; it’s only Revise’s own internal compilation time that I’m discussing here.

As part of the latency-reduction, Revise no longer tracks its own code or that of its dependencies. Call `Revise.add_revise_deps()` after startup if you want it to track itself.

### Better error handling and more Julian error messages

The error-handling system has been extensively reworked. While there are still some printing differences between errors with and without Revise (mostly from using `@error` rather than `error(...)`), the messages and stack traces should now be very similar. It should also be less intrusive: now, only the first error triggered in a file is printed, and Revise warns you of outstanding revisions only by coloring your prompt yellow until you’ve resolved the outstanding errors.

### More robustness when making revisions

Revise 2, while quite capable, still found ways to make annoying mistakes. For example, it used a somewhat arbitrary order to enact its revisions, and sometimes it got the order wrong. In Revise 3, if PkgA depends on PkgB, then PkgB’s revisions will be processed before PkgA’s, ensuring that new definitions in PkgB will be available for changes in PkgA. Likewise, if MyPkg loads `"file1.jl"` before it loads `"file2.jl"`, it will process `"file1.jl"`'s revisions before processing those of `"file2.jl"`. There are still corner cases that can break this (e.g., when `"file1.jl"` does an `include("file2.jl")` partway through and then uses some newly-defined types in `"file2.jl"`), but the number of opportunities for breakage should be vastly reduced.

A similar improvement concerns the process moving methods from one file to another, something that is fairly common when you’re working on larger packages or ecosystems. With Revise 2, unless you remembered to complete the move (copying to the destination _and_ deleting from the source) before triggering a revision, Revise would “helpfully” delete the entire method from your session when you got around to deleting it from its original location. Revise 3 fixes this by (transiently) supporting multiple locations for method definitions, and deleting methods only when their _last_ instantiation is gone.

### Selective evaluation

From a technical standpoint, the most impressive new feature of Revise 3 is support for selective evaluation. ~~This is an area where the defaults could still change depending on user feedback, and one of the major motivations for this beta period is to find out what choices work best in practice before locking them in throughout the Revise 3.x release cycle.~~

The short version of this feature can be summed up as follows: here on discourse, it’s been pretty common to advise users of `includet` to split their files, with “code” going in a package and “data processing” going in a script that does not get tracked by Revise. In Revise 3, this essentially happens automatically: Revise will (subject to alteration by some user-configurable settings) refuse to alter _data_ but will happily modify _methods_, even when the two are densely interwoven.

Here’s the longer version: when defining methods, Julia keeps track of “dependencies,” so that if `foo` calls `bar` and you redefine `bar`, then `foo` will automatically be recompiled the next time you call it. We do not have the same notion of dependencies for _data_: if I used `x` to compute `y`, should I re-compute `y` when `x` changes? In Revise 2, the answer was “re-evaluate every changed expression and don’t worry about any dependencies.” That works sometimes, but sometimes causes big problems: either you got changes you didn’t want (forcing you to recompute the setup for your problem from scratch again), and/or long computation times since Revise performs changes using the interpreter.

Currently in the beta, the defaults are to re-evaluate _all changes_ in packages and only _method definitions_ in `includet` scripts. In other words, in `includet` scripts Revise will re-evaluate methods but _it will not touch your data_. That’s true even if you explicitly change a line defining a variable, because Revise doesn’t know what _else_ you might need to recompute in order to stay consistent; if you want to modify data, you need to take care of it yourself. Fortunately, with both major IDEs now supporting inline evaluation, this is easier than ever. (An alternative is to alter Revise’s settings, described below.)

Perhaps the best way to illustrate this is with a demo; create a file

```julia
a = rand() # "data": will not be revised
b = 2 # "data": will not be revised even if you change it
f() = 1 # "method": will be revised
for (T, varname, e) in ((Float32, :c, rand()), (Float64, :d, rand()))
    @eval begin
        $varname = rand() # hiding data inside an @eval doesn't fool Revise!
        g(::$T) = $e # and it can still revise methods
    end
end

```

and then do this in your session:

```julia
julia> @show a b c d
a = 0.14642990011901702
b = 2
c = 0.6799833688203913
d = 0.12299256361483968
0.12299256361483968

julia> f()
1

julia> g(1.0)
0.6651919620816014

julia> g(1.0) # the value is static
0.6651919620816014

julia> g(1.0f0)
0.14537943287044364

julia> g(1.0f0)
0.14537943287044364

```

Now, change the value assigned to `b` and all the methods by modifying the script to

```julia
a = rand() # "data": will not be revised
b = 3 # "data": will not be revised even if you change it
f() = 2 # "method": will be revised
for (T, varname, e) in ((Float32, :c, rand()), (Float64, :d, rand()))
    @eval begin
        $varname = rand() # hiding data inside an @eval doesn't fool Revise!
        g(::$T) = $e+1 # and it can still revise methods
    end
end

```

and then display the same values:

```julia
julia> @show a b c d
a = 0.14642990011901702
b = 2
c = 0.6799833688203913
d = 0.12299256361483968
0.12299256361483968

```

which you can see are unchanged (even though some are interwoven with method definitions), but all the methods have been redefined:

```julia
julia> f()
2

julia> g(1.0)
1.4382250538965402

julia> g(1.0f0)
1.4809866331295454

```

Some of the cool magic here is more obvious if you notice that Revise did need to assign values to some variables, notably `T` and `e` as it iterated through the loop, but `c` and `d` were not involved in method definition and hence were not modified.

If you don’t like the defaults, you can set ` __revise_mode__ = :evalassign` and Revise will also evaluate changed assignments; ` __revise_mode__ = :eval` will evaluate everything. But keep in mind that some of these settings will trigger new `rand()` numbers being assigned in some of those assignments above.

Again, by default this new mode only applies to `includet`: currently packages default to ` __revise_mode__ = :eval`. You can set the mode of packages on a per-module basis by setting ` __revise_mode__ ` within the module.

~~If everybody hates the new defaults we can change them before Revise 3.0 release, but I will be reluctant to make changes after the release. So this is a good time to take Revise 3 for a spin and tell me what you like and what you dislike.~~

Those are the major changes; let the testing begin!

---

<div class="post-metadata">

**Author:** ![oschulz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oschulz/32/2998_2.png) [@oschulz](https://discourse.julialang.org/u/oschulz)\
**Post date:** [September 8, 2020, 7:39pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/2 "2020-09-08T19:39:51Z")

</div>

> [@tim.holy](#):
>
> Revise#revise3

@tim.holy, would it be possible to have a branch of Rebugger compatible with Revise v3 already? Or is it too early for that?

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [September 8, 2020, 7:43pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/3 "2020-09-08T19:43:41Z")

</div>

Too early. In fact probably ~~none of the debuggers~~ only Debugger.jl is already compatible with JuliaInterpreter 0.8, which is required for Revise 3. However, I expect that to come fairly shortly, with the exception of Rebugger which needs more help from the community for it to stay maintained.

---

<div class="post-metadata">

**Author:** ![oschulz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oschulz/32/2998_2.png) [@oschulz](https://discourse.julialang.org/u/oschulz)\
**Post date:** [September 8, 2020, 7:45pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/4 "2020-09-08T19:45:24Z")

</div>

Thanks for the heads up.

Oh - and this is amazing! I can hardly imagine Julia without Revise - well, I can, but I really don’t want to. 🙂

---

<div class="post-metadata">

**Author:** ![musm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/musm/32/3675_2.png) [@musm](https://discourse.julialang.org/u/musm)\
**Post date:** [September 9, 2020, 5:38pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/5 "2020-09-09T17:38:07Z")

</div>

Is it possible the configure or change ` __revise_mode__ ` for `includet` ? I tried setting  
` __revise_mode__ = :evalassigned` within the file, yet it seems `includet` isn’t picking up assignment changes.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [September 9, 2020, 5:39pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/6 "2020-09-09T17:39:30Z")

</div>

Anywhere in `Main` should be fine, in theory. If you’re changing it in the file _after_ `includet`ing the file, then remember Revise won’t track the assignment so you won’t actually make it!

Oh, it’s also `:evalassign` and _not_ `:evalassigned`.

Quick summary:

```julia
__revise_mode__ = :evalassign
a = [1] # this line gets modified for :eval or :evalassign
push!(a, 2) # this line gets modified only for :eval
f() = 1 # this line gets modified for :eval, :evalassign, or :evalmeth

```

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [September 9, 2020, 10:41pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/7 "2020-09-09T22:41:32Z")

</div>

> [@tim.holy](#):
>
> Revise warns you of outstanding revisions only by coloring your prompt yellow until you’ve resolved the outstanding errors.

I love this

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [September 9, 2020, 10:48pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/8 "2020-09-09T22:48:24Z")

</div>

> [@tim.holy](#):
>
> Currently in the beta, the defaults are to re-evaluate _all changes_ in packages and only _method definitions_ in `includet` scripts. In other words, in `includet` scripts Revise will re-evaluate methods but _it will not touch your data_ .

Should we have two different `includet` functions?

One for methods + data, and one only for methods?

I personally don’t use `includet` so I have no stake in this game.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [September 9, 2020, 11:13pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/9 "2020-09-09T23:13:13Z")

</div>

> Should we have two different `includet` functions? One for methods + data, and one only for methods?

Currently you can achieve that this way:

```julia
module JustMethods

__revise_mode__ = :evalmeth

# code

end

module MethodsAndData

__revise_mode__ = :eval

# more stuff

end

```

Then `includet` this file.

Of course one of these two modules could be `Main` if you don’t want the module barrier.

> I personally don’t use `includet` so I have no stake in this game.

I don’t really use it either, although I suspect I’ll use it more now especially for writing tests. A nice use of the new functionality:

- create a blank test file, perhaps with a few “helper” methods you expect to be useful in writing good tests
- `includet` the test file
- now start adding `@testset`s to the file

(You can `includet` a test file that already has `@testset`s in it, but you need to make sure the whole file runs successfully before Revise will track it.)

Now, this is pretty much a disaster with Revise 2: every time you revise a testset, the testset runs, and doesn’t run when you change your helper methods. With Revise 3, the helper methods will update but you use Alt-Enter in vscode when you want to try a testset. I find it to be a much nicer workflow, because you control when you want to run the tests.

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [September 10, 2020, 10:34am UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/10 "2020-09-10T10:34:19Z")

</div>

> [@tim.holy](#):
>
> I don’t really use it either, although I suspect I’ll use it more now especially for writing tests.

Incidentally, one of my typical workflows involves using `Revise.entr` to automatically re-run(\*) a stub test file each time a change in the package code is picked up by Revise.

This way, I can start working simultaneously on (1) a function implementing a feature in a package and (2) a typical intended use of that function. Each time I make incremental changes to the function, I get to immediately see how the output changes (without having to switch to the tests file and hitting Alt+Ret to run the relevant line). When I’m satisfied, I can easily turn that simple use case into a unit test.

  

* * *

(\*) such stub test files typically contain only “data processing” statements, and with Revise2 I would simply re-`include` them.

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [September 10, 2020, 3:43pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/11 "2020-09-10T15:43:41Z")

</div>

FYI: I had to remove @essenciary’s Genie.jl package, and Atom. Juno seemed ok with that, just downloaded stuff, and started seemingly ok without Atom.jl. I’m however trying out VSCode anyway, and it’s the future, so I’m ok.

I’m not sure it’s right for Genie (or the ton of other packages having Revise as its dependency). Or should it allow, right now the beta? I thought Revise was just for interactive/your startup.jl file.

For Genie, Revise direct dependency, while [juliahub.com](http://juliahub.com) doesn’t show me how Atom (or packages below) is related to Revise (juliahub needs to update regularly?). ~~Is it indirectly tied to Julia itself?~~ [seems just mentioned there, and only tested with.]:

```julia
(@v1.6) pkg> add Revise#revise3
   Updating git-repo `https://github.com/timholy/Revise.jl.git`
  Resolving package versions...
ERROR: Unsatisfiable requirements detected for package JuliaInterpreter [aa1ae85d]:
 JuliaInterpreter [aa1ae85d] log:
 ├─possible versions are: 0.1.1-0.8.0 or uninstalled
 ├─restricted to versions 0.8 by Revise [295af30f], leaving only versions 0.8.0
 │ └─Revise [295af30f] log:
 │ ├─possible versions are: 3.0.0 or uninstalled
 │ └─Revise [295af30f] is fixed to version 3.0.0
 └─restricted by compatibility requirements with Atom [c52e3926] to versions: 0.7.10-0.7.26 — no versions left
   └─Atom [c52e3926] log:
     ├─possible versions are: 0.8.0-0.12.21 or uninstalled
     ├─restricted to versions * by an explicit requirement, leaving only versions 0.8.0-0.12.21
     └─restricted by compatibility requirements with CodeTracking [da1fd8a2] to versions: 0.12.15-0.12.21 or uninstalled, leaving only versions: 0.12.15-0.12.21
       └─CodeTracking [da1fd8a2] log:
         ├─possible versions are: 0.1.0-1.0.2 or uninstalled
         └─restricted to versions 1 by Revise [295af30f], leaving only versions 1.0.0-1.0.2
           └─Revise [295af30f] log: see above

```

---

<div class="post-metadata">

**Author:** ![essenciary](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/essenciary/32/210469_2.png) [@essenciary](https://discourse.julialang.org/u/essenciary)\
**Post date:** [September 10, 2020, 4:23pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/12 "2020-09-10T16:23:38Z")

</div>

Yes, Genie uses Revise to make user code (within Genie apps) revisable so that editing the app “just works”. I think I can refactor it out and only keep Revise as a dependency of the Genie app (not of Genie itself). I’ll take a look.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [September 16, 2020, 2:43pm UTC](https://discourse.julialang.org/t/ann-revise-3-0-faster-and-better/46287/13 "2020-09-16T14:43:38Z")

</div>

Revise 3 is released. Thanks to the testers for the feedback!

Compared to the beta there was a bug or two fixed, and the performance of handling `@require` blocks was greatly improved. If you’re loading more than just a small handful of packages, Revise’s overhead should be pretty negligible.
