MethodErrors suggest fixes that may depend on internal bindings

On Julia v1.11:

julia> using FillArrays, LinearAlgebra

julia> Zeros(2,2) * UpperTriangular(Zeros(2,2))
ERROR: MethodError: *(::Zeros{Float64, 2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}, ::UpperTriangular{Float64, Zeros{Float64, 2, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}}}) is ambiguous.

Candidates:
  *(a::FillArrays.AbstractZeros{T, 2} where T, b::AbstractMatrix)
    @ FillArrays ~/.julia/packages/FillArrays/p9YG6/src/fillalgebra.jl:89
  *(A::AbstractMatrix, B::LinearAlgebra.AbstractTriangular)
    @ LinearAlgebra ~/packages/julias/julia-latest/share/julia/stdlib/v1.11/LinearAlgebra/src/triangular.jl:1501

Possible fix, define
  *(::FillArrays.AbstractZeros{T, 2} where T, ::LinearAlgebra.AbstractTriangular)

Stacktrace:
 [1] top-level scope
   @ REPL[6]:1

The suggestion is to define a method that dispatches on LinearAlgebra.AbstractTriangular. If I check what that is, I obtain

help?> LinearAlgebra.AbstractTriangular
  │ Warning
  │
  │  The following bindings may be internal; they may change or be removed in future versions:
  │
  │    •  LinearAlgebra.AbstractTriangular

  No documentation found for private symbol.

This doesn’t inspire much confidence in the proposed fix. I’m unsure what’s the best solution is in general, but in this specific example, the resolution might be to define methods for UpperTriangular and LowerTriangular separately.

I’d say the issue is that types used in a public API call should also be public.

2 Likes

Oh boy. I have a feeling these new “Possible fix” messages are going to inspire a lot of type piracy among inexperienced users.

These messages are not new, they were already present on v1.3 and perhaps even earlier.

For completeness: the above can be easily generated with a MWE like this:

julia> abstract type Super end

julia> struct A <: Super end

julia> struct B <: Super end

julia> foo(a::A, b::Super) = 1
foo (generic function with 1 method)

julia> foo(a::Super, b::B) = 2
foo (generic function with 2 methods)

julia> foo(A(), B())
ERROR: MethodError: foo(::A, ::B) is ambiguous.

Candidates:
  foo(a::A, b::Super)
    @ Main REPL[4]:1
  foo(a::Super, b::B)
    @ Main REPL[5]:1

Possible fix, define
  foo(::A, ::B)

Stacktrace:
 [1] top-level scope
1 Like

Yeah, I meant the warning about the symbols not being public is new, whereas the situation was more nebulous earlier. They were never public, but there was no explicit warning that these may be removed.

I guess the solution is to nudge packages to not use private types in dispatch for user-facing methods, as suggested above. This may be done through Aqua checks.

I think an ambiguity error would only ever suggest an internal/private binding if the package is already using that internal/private binding.