Because of the upcoming world age-partitioned semantics (PR #57253), Revise can pull off type redefinitions by reassigning the const
name (Issue #18).
Let’s say we start with:
julia> begin
struct X{T} x::T end;
unwrap(a::X) = a.x; # argument annotation, body uses field
square(a::X) = unwrap(a) ^ 2; # argument annotation
struct Y{T} y::X{T} end; # field annotation, uses a parameter
wrap(x) = X(x); # const X name in body
wrap(1)
end
X{Int64}(1)
Now we’re allowed to re-evaluate the struct definition (in Revise we’d just edit the source file):
julia> struct X end; # removes parameter and field
The obsolete type object still exists, which makes sense considering that there may still be instances around. Let’s look at the consequences for everything else:
julia> methods(unwrap) # not reevaluated, good because body needs change
# 1 method for generic function "unwrap" from Main:
[1] unwrap(a::@world(X, 38433:38439))
@ REPL[2]:1
julia> methods(square) # not reevaluated, could be because it fits new type
# 1 method for generic function "square" from Main:
[1] square(a::@world(X, 38433:38439))
@ REPL[3]:1
julia> dump(Y) # not reevaluated, good to avoid TypeError
UnionAll
var: TypeVar
name: Symbol T
lb: Union{}
ub: abstract type Any
body: struct Y{T} <: Any
y::@world(X, 38433:38439){T}
julia> wrap(1) # invalidated but fails, good because needs change
ERROR: MethodError: no method matching X(::Int64)
...
So besides invalidating methods like wrap
that use the name X
, the type redefinition doesn’t change annotations because the definitions used the type object at the time, not the name itself. In the case of unwrap
and Y
, that’s fine because we need to edit and reevaluate to make things work anyway. However, square
happens to not need edits for its reevaluation, which makes things difficult for Revise’s file-editing design. It’d be strange to add useless lines just to trigger a change, what will be the course of action there?
Separately tracking variables used at definition-time and annotation structure to automatically reevaluate suitable definitions is not a solution because we’re allowed to reassign const X = 1
or anything that isn’t a type, which will throw errors upon any annotations. That’s a lot of overhead for nothing. However, it does seem nice when we’re just changing types and have many definitions like square
that happen to fit the newer type.