Concrete vs parametric when determining type specificity

I had a brief look through the source code, and it indeed looks like arguments are considered independently when determining specificity.

My reading of the source code involved a lot of educated guessing, so here’s my reasoning in case anyone wants to double-check:

  • According to the dev docs, jl_type_morespecific(a,b) is the function which determines method specificity.

  • This function first does a couple of checks that AFAICT don’t apply to our case, and then calls type_morespecific().

    JL_DLLEXPORT int jl_type_morespecific(jl_value_t *a, jl_value_t *b)
    {
        if (obviously_disjoint(a, b, 1))
            return 0;
        if (jl_has_free_typevars(a) || jl_has_free_typevars(b))
            return 0;
        if (jl_subtype(b, a))
            return 0;
        if (jl_subtype(a, b))
            return 1;
        return type_morespecific_(a, b, a, b, 0, NULL);
    }
    
  • Both inputs are tuples, so type_morespecific() dispatches to tuple_morespecific():

    static int type_morespecific_(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_value_t *b0, int invariant, jl_typeenv_t *env)
    {
        # ...
        if (jl_is_tuple_type(a) && jl_is_tuple_type(b)) {
            # ...
            return tuple_morespecific((jl_datatype_t*)a, (jl_datatype_t*)b, a0, b0, invariant, env);
        }
        # ...
    }
    
  • tuple_morespecific() is quite complicated, but most of the decision-making seems to be based on a variable called some_morespecific, which to me sounds a lot like we’re checking whether some of the elements in the first tuple are more specific than their counterparts in the second tuple.

1 Like

How do you check if a type satisfy jl_is_tuple_type, the diagonal constraint should cause it not to be a tuple type?

That was one instance of educated guessing :see_no_evil:

Basically, I used this as a proxy:

julia> methods(foo)[1].sig <: Tuple
true

It is of course entirely possible that that’s not the test used by jl_is_tuple_type(), but it’s the best I could come up with.

As far as I can tell, your reasoning is correct & consistent with what I described above :slight_smile: To be fair, I also only learnt about how this works by reading the source code & piecing it together with the description in the devdocs.

All function signatures are tuple types. That’s why I translated the foo(::T, Vector) to Tuple{T, Vector{S}} where {T,S} above.

1 Like