Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specificity of function not clear #46598

Closed
lcnbr opened this issue Sep 2, 2022 · 5 comments
Closed

Specificity of function not clear #46598

lcnbr opened this issue Sep 2, 2022 · 5 comments

Comments

@lcnbr
Copy link

lcnbr commented Sep 2, 2022

I have the following constructors

abstract type A end
abstract type B <: A end  
struct S<:B
  field1
  field2
end
struct R<:B
  field1
  field2
end

function (::Type{T})(f::T) where {T<: A} 
  @info "no copy"
  f
end

function (::Type{T})(f::B) where T <: B
  @info  "copy"
  T(f.field1,f.field2)  
end

Now if I execute

r=R(2,3)
S(r) #copy
R(r) #copy

I would expect that R(r) calls the first constructor but that is not the case. It seems to me that the first will always be more specific..
If anything I cannot find any info on the topic in the documentation. Maybe something needs clearing up in the diagonal types section..

@martinholters
Copy link
Member

Note that this has nothing to do with constructors. The same specificity can be exercised with

julia> abstract type A end

julia> abstract type B <: A end

julia> struct S<:B end

julia> struct R<:B end

julia> foo(::Type{T}, x::T) where {T <: A} = 1
foo (generic function with 1 method)

julia> foo(::Type{T}, x::B) where {T <: B} = 2
foo (generic function with 2 methods)

julia> foo(S, R())
2

julia> foo(R, R())
2

To quote from https://docs.julialang.org/en/v1/devdocs/types/#Subtyping-and-method-sorting: "If a is a strict subtype of b, then it is automatically considered more specific. From there, type_morespecific employs some less formal rules."

Here we have two conflicting aspects: As B<:A, the second method should be more specific, but only the first method requires x::T, so that one should be more specific. The "less formal rule" here seems to be to favor the second method.

The fix/workaround would be to define

foo(::Type{T}, x::T) where {T <: B} = 3

or

function (::Type{T})(f::T) where {T<: B} 
  @info "no copy"
  f
end

for your case.

@lcnbr
Copy link
Author

lcnbr commented Sep 2, 2022

Hmm okay, thanks! Are these less formal rules documented somewhere then? Because to me at least, it should be opposite specificity..

@lcnbr lcnbr changed the title Specificity of constructor not clear Specificity of function not clear Sep 2, 2022
@jakobnissen
Copy link
Contributor

Duplicate of #23740, I think

@jakobnissen
Copy link
Contributor

Interestingly:

julia> f(::Vector{<:Integer}, ::Int) = 1
f (generic function with 1 method)

julia> f(::Vector{Int}, ::Integer) = 2
f (generic function with 2 methods)

julia> f([1], 1)
ERROR: MethodError: f(::Vector{Int64}, ::Int64) is ambiguous.

I would imagine this should behave the same, but apparently not.

@vtjnash
Copy link
Member

vtjnash commented Sep 2, 2022

More-specific first ignores TypeVar. It sees that the first case requires both args to be <:A and in the second case to be <:B. Since B is more specific than A, it is the more-specific method.

@vtjnash vtjnash closed this as completed Sep 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants