Revise doesn't work with macros?

Can it be true that Revise doesn’t work with macros.
I experienced the problem when developing a package that has functions that use a macro. Then when I change the macro, the functions are not updated.

But a similar minimal working example can also be shown with includet:

# revisemacro.jl

macro returniffalse(verbose, ex)
    quote
        if !($(esc(ex)))
            if $(esc(verbose)) 
               println("A False expression in $(@__FILE__):$(@__LINE__): ", $(string(ex)))
            end
            return false
        end
    end
end

function afunction()
	avariable = 23
	@returniffalse(true, avariable == 20+1)
end

Open a REPL and do

julia> using Revise;

julia> includet("revisemacro.jl");

julia> afunction()
A False expression in /home/pakis/Playground/julia/ReviseMacro/revisemacro.jl:5: avariable == 20 + 1
false

Then if I change the macro in revisemacro.jl the function is not updated

# revisemacro.jl

macro returniffalse(verbose, ex)
...
              # println("A False expression in $(@__FILE__):$(@__LINE__): ", $(string(ex)))
              println("NEW STUFF $(@__FILE__):$(@__LINE__): ", $(string(ex)))
...
end
...

The REPL still prints the old information

julia> afunction()
A False expression in /home/pakis/Playground/julia/ReviseMacro/revisemacro.jl:5: avariable == 20 + 1
false
1 Like

It’s documented in the Limitations page:

Macros and generated functions
If you change a macro definition or methods that get called by @generated functions outside their quote block, these changes will not be propagated to functions that have already evaluated the macro or generated function.
You may explicitly call revise(MyModule) to force reevaluating every definition in module MyModule. Note that when a macro changes, you have to revise all of the modules that use it.

Revise.revise of course won’t work for a includet file because it’s not a tracked module.

Revise only makes it easier to do the same interactive evaluations you can manually run in the REPL, and despite the extra options for more reevaluations, that can never behave like reloading the session and evaluating everything from scratch. For example, you can’t successfully change the annotated type of a global variable. Redefinitions of methods called by other methods work smoothly because the call is not executed during definition. If you edit a method definition, previous calls are not reevaluated; the same applies to macro methods. Here’s a macro-less example:

foo() = 1
_foo::Int = foo()
bar() = _foo # instead of bar() = foo()

Even with __revise_mode__ = :eval, _foo::Int = foo() is not reevaluated when foo() = 2.3 is edited because its expression was not changed; if it was, it’d fail anyway.

4 Likes

My preferred workaround to this is to have the implementation in a separate function which is called by the macro:

function _foo(expr)
    return :(x = $expr)
end

macro foo(expr)
    _foo(expr)
end

That way you can still take advantage of Revise, and in some cases it makes testing easier because you can just call the function with different inputs.

Editing _foo doesn’t seem to re-evaluate @foo calls in other methods (example below) like filchristou wanted, could you elaborate with a demo? PS I think we need esc(_foo(expr)).

function bar(y)
  @foo y
  x
end