Why isn't this legal syntax for defining a subtype?

I have some abstract type parameterised by a T type like this

abstract type SomeAbstractType{T} end

and I want to create a subtype from it and this syntax works

mutable struct Some{T<:Number} <: SomeAbstractType{T}
    somefield::T
end

So in the above, are the two Ts referring to the something?

I would have thought that the below work

mutable struct Some{T} <: SomeAbstractType{T} where {T <: Number}
    somefield::T
end

but it doesn’t work and gives ERROR: invalid subtyping in definition of Some: can only subtype data types.

I don’t really understand what’s going on here. What’s the theoretical reason why the 2nd one doesn’t work?

1 Like

I think you can put the T <: Number inside one of the other {T}, I can’t remember which one.

And remove the where {... statement altogether.

yeah. that seems to be some kind of complexity. You mean the example I showed that worked?

Oh :sweat_smile: yes. My weak short term memory at demonstration there sorry.

I guess then the question is, what extra information do you expect your non-working example to provide the compiler that isn’t in the working example?

Or are you saying that it’s reasonable that your non-working example should be valid syntax? If so then, I suppose I’d agree in my limited perspective.

i am just confused why it isn’t valid.

1 Like

It does not seem to be valid Julia syntax to add the where clause to the struct definition.

This works

abstract type SomeAbstractType{T} end

mutable struct Some{T} <: SomeAbstractType{T where {T <: Number}}
      somefield::T
end

The question is: is this what you intended?

4 Likes

My understanding is a bit limited, but it used to be that both function and struct definitions used similar syntax for type parameters:

struct Foo{T<:Number}
   ... 
end

function bar{T<:Number}(x::T) 
   ... 
end

The where clause was introduced later (~v0.7?) in order to improve certain things that was difficult to express clearly with the old syntax. Currently, the syntax Foo{SomeType}(x) necessarily means you are looking at a constructor.

So where is a newer syntax, introduced out of a need, but, imo, less aesthetically pleasing than the old syntax.

2 Likes

Yeah

…to which the answer is almost certainly: No, because that makes every Some, including, e.g. Some{String} a subtype of SomeAbstractType{Number}.

2 Likes

The curly braces immediately following the struct’s name is the parametric type’s where clause, and it doesn’t make sense to allow another location for a possibly different where clause.

julia> struct Y{N isa Int} end # proof by error message
ERROR: syntax: invalid variable expression in "where" around REPL[64]:1

The 2nd one could be a valid alternative syntax, but Julia decided on the 1st. The 1st obviously saves a bit of typing, but the bigger advantage is the clarity that the clause is limiting the parameters of the defined type, not its declared supertype. This is not like methods’ where clauses limiting all preceding arguments.

This is exactly equal to SomeAbstractType{Number}, the T effectively disappears and is unrelated to the parameter in Some{T}. This is verified to occur in type definitions:

julia> mutable struct X{T} <: Ref{T where {T <: Number}}
           somefield::T
       end

julia> dump(X)
UnionAll
  var: TypeVar
    name: Symbol T
    lb: Union{}
    ub: Any
  body: X{T} <: Ref{Number}
    somefield::T 
1 Like

Perhaps then what was meant was:


julia> abstract type SomeAbstractType{T} end
julia> struct A
       a::Int
       end
julia> mutable struct Some{T2<:Number} <: SomeAbstractType{A}
                  somefield::T2
              end
1 Like

The docstring of where says it’s an iteration to produce a Union.

Does this suggest that usage of where in struct definitions doesn’t make sense because it would mean you’re making multiple definitions at once which means they all conflict?

Shouldn’t this have been a new topic?

Anyway, “iterated union” refers to UnionAll, not to Union. Making a PR. EDIT: here it is:

2 Likes

Thanks for the PR.

The original question somewhat asks why the where keyword isn’t valid for use in struct definitions. I thought it kind of makes some sense to bring up the docstring to address the original question. Am I wrong?

Technically yes. First, where’s docstring says “iterated union”, not the type Union. The Manual describes UnionAll as iterated unions, hence the PR to clarify this in the docstring. Second, the first optional curly braces in the type definition’s header is the type’s where clause, I showed this earlier in the thread. There’s no implication of making multiple definitions, just one definition of an iterated union of types.

But actually no, this is relevant discussion, at least in my opinion. A bit late, but I don’t think it’s been long enough to firmly justify a new thread either.

2 Likes

Thanks for the clarification, evidently I didn’t fully understand some of the discussion, hence my question and if I was in the right direction (which I wasn’t), some suggestion on why. Thanks again.