Revise.jl's `includet` gives error when `include` doesn't

I have a .jl file where I define struct S that I call from another .jl file with using Revise; includet("path_to_file").

If this is my definition of S, I do not get an error:

struct S{N}
    x::Array{<:Real, N}

    S{N}(x::Array{<:Real, N}) where N = new{N}(x)
end

If I add another inner constructor, I get ERROR: invalid redefinition of type S even when I’m in a completely new Julia session.

struct S{N}
    x::Array{<:Real, N}

    S{N}(x::Array{<:Real, N}) where N = new{N}(x)
    S(x::Array{<:Real, N}) where N = S{N}(x)
end

If I change the syntax to this, the error goes away:

struct S{N}
    x::Array{<:Real}{N} where N

    S{N}(x::Array{<:Real}{N}) where N = new{N}(x)
    S(x::Array{<:Real}{N}) where N = S{N}(x)
end

I do not get the error for any of the three cases if I use include instead of Revise.jl’s includet.

Am I doing something wrong?

julia> versioninfo()
Julia Version 1.9.0
Commit 8e630552924 (2023-05-07 11:25 UTC)
Platform Info:
  OS: macOS (arm64-apple-darwin22.4.0)
  CPU: 8 × Apple M1
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-14.0.6 (ORCJIT, apple-m1)
  Threads: 1 on 4 virtual cores
Environment:
  JULIA_EDITOR = code
  JULIA_NUM_THREADS = 
1 Like

I know this is as valid as Vector{T} but it hurts my eyes

I think it hurts Julia’s eyes too. [1] isa Array{<:Real}{1} gives me an error, actually. [1] isa Array{<:Real, 1} does not. I thought they were the same thing.

julia> Array{<:Real}{1}
ERROR: TypeError: in Array, in #s2, expected var"#s2"<:Real, got a value of type Int64

julia> Array{<:Real}{1, Int64}
ERROR: TypeError: in Array, in #s2, expected var"#s2"<:Real, got a value of type Int64

julia> Array{<:Real}{Int64, 1}
Vector{Int64} (alias for Array{Int64, 1})

Is it this error, then scratch what I said earlier, I think {1} tries to put the 1 in the first type parameter. This is consistent with how the where clause of a type union completely ignores the type parameters of a parametric struct, even when it would contain types impossible to instantiate:

julia> struct X{T<:Real} end; X{S} where {S<:String}
X{S} where S<:String

I don’t know if the {1} in Array{T}{1} is specifying the first type parameter.

julia> Array{Int64}{1}
Vector{Int64} (alias for Array{Int64, 1})

[1] isa Array{<:Real}{1} gives an error but:

julia> [1] isa Array{Int64}{1}
true

Hm, then maybe it’s because Array{Int64} only has 1 parameter left to specify? But Array{<:Real} actually has both its parameters unfixed, so the 1 is attempted to be set as T<:Real, and 1 does not subtype Real. Array{<:Real}{Int64} works, and Array{<:Real}{String} fails. Array{<:Real}{N} where N is not trying to set T, it’s just replacing the parameter bounds <:Real with N<:Any.

As for the original topic, I can replicate the behavior, if I restart the session and run includet again each time. However, I will point out that when I check methods(S) and methods(S{1}), both methods are there and they work just fine S([1]), S{1}([1]). I really have no idea why the error is printed.

On the other hand, if I am running just 1 session and am just editing the file to make those changes, adding that other inner constructor adapts the methods just fine. However, changing the type annotations with the double brackets only seems fine at first, but then if I try methods(S) I get a pretty sticky Failed to Revise error and all the methods are gone. I must edit the file to the 2nd version, not the 1st, to get rid of that error, and even then the methods don’t come back. This is in line with if I was re-includeing the file instead, I’m allowed to add methods but not change the other parts of the struct.

Thanks for replicating. I opened an issue.