Redefining structs without restart?

I worked around the limitation of the displayed name with the new macro

macro redefinable(struct_def)
    struct_def isa Expr && struct_def.head == :struct || error("struct definition expected")
    if struct_def.args[2] isa Symbol
        name = struct_def.args[2]
        real_name = struct_def.args[2] = gensym(name)
    elseif struct_def.args[2].head == :<:
        name = struct_def.args[2].args[1]
        real_name = struct_def.args[2].args[1] = gensym(name)
    end
    esc(quote
        $struct_def
        $name = $real_name # this should be `const $name = $real_name`
        Base.show_datatype(io::Base.IO, ::Base.Type{$real_name}) = Base.print(io, $(QuoteNode(name)))
    end)
end

A small example:

abstract type A end

foo(a::A) = (a, a.x)

@redefinable struct T <: A
    x::Int
end

@show foo(T(42))
# (T(42), 42)

@redefinable struct T <: A
    x::Float64
end

@show foo(T(3.14))
# (T(3.14), 3.14)

Again, this implements the semantic of Option 1: there are two independent types (secretly named ##T#***) that know nothing about each other, and the name T in the current namespace switches from one to the other.

Now, there is still a problem with this macro: the third to last line should be const $name = $real_name, so that usage of $name (T in the example) would be inlined by the compiler.


Edit

In the macro, instead of the Base.show_datatype hack, one can assign to $real_name.name.name, that is, the return value of the macro becomes

    esc(quote
        $struct_def
        $real_name.name.name = $(QuoteNode(name)) # fix the name
        $name = $real_name # this should be `const $name = $real_name`
    end)
1 Like