Is it possible to define a type-stable function that returns a Plots.Plot object?

Is it possible to define a type stable function that returns a Plots.plot object?
Minimal reproducible example:

using Plots
function mplot(vec::AbstractVector)
  plot(vec)
end

@code_warntype mplot(rand(10))

returns


P.S.: I know the following is possible. I’m curious about another coding style

function mplot!(plt, vec::AbstractVector)
  plot!(plt, vec)
end
a = plot()
@code_warntype mplot!(a, rand(10))

returns

Without knowing anything about the internals of Plots.jl it looks like the plot function is inherently type-unstable (it returns a Plots.Plot, but without specifying the type parameter parameter/backend, it cannot be inferred to be a specific Plots.Plot{SomeBackend}).

I am only aware of setting the plot backend via functions (gr(), etc.), but maybe there are some ways to force plot to be more inferrable?

(If you just want to make your function type stable in the sense that the output type can be inferred “from the outside”, you could add a type assertion and hard-code the backend

julia> function mplot2(vec::AbstractVector)
         plot(vec)::Plots.Plot{Plots.GRBackend}
       end
mplot2 (generic function with 1 method)

julia> @code_warntype mplot2(rand(10))
MethodInstance for mplot2(::Vector{Float64})
  from mplot2(vec::AbstractVector) @ Main REPL[27]:1
Arguments
  #self#::Core.Const(mplot2)
  vec::Vector{Float64}
Body::Plots.Plot{Plots.GRBackend}
1 ─ %1 = Main.plot(vec)::Plots.Plot
│   %2 = Plots.Plot::Core.Const(Plots.Plot)
│   %3 = Plots.GRBackend::Core.Const(Plots.GRBackend)
│   %4 = Core.apply_type(%2, %3)::Core.Const(Plots.Plot{Plots.GRBackend})
│   %5 = Core.typeassert(%1, %4)::Plots.Plot{Plots.GRBackend}
└──      return %5

)

This is a good solution in this case, but it isn’t enough in some cases like:

function mplot(vec::AbstractVector)
  p::Plots.Plot{Plots.GRBackend} = plot(vec)
  plot!(p, legend=true)
end

# Or

function mplot(vec::AbstractVector)
  p::Plots.Plot{Plots.GRBackend} = plot(vec)
  plot!(p, legend=true)::Plots.Plot{Plots.GRBackend}
end

# Or

function mplot(vec::AbstractVector)
  plot(vec)
  plot!(plegend=true)::Plots.Plot{Plots.GRBackend}
end

Could you elaborate please? @code_warntype reports essentially the same behavior for me in all three cases (the initial Plots.Plot object is marked red, but the overall return type of the function is known), so I’m not sure how these cases differ from the original.

Another option might be to use the underlying plotting backend directly, e.g. don’t use Plots.jl, but rather GR.jl since you have to specify the backend anyway, as it seems like (unless you add the backend as an input to the function of course).