Keyword function error inverts position of arguments?

I just spend a lot of time figuring out why this function here

function solve(ncgm::NeoclassicalGrowth, vp::ValueCoeffs{T};
        tol::Float64 = 1e-06, maxiter::Int = 5000, dampen::Float64 = 1,
        nskipprint::Int = 1, verbose::Bool = true) where {T <: SolutionMethod}

would not for all of my efforts compile. I was trying to make sense of this error message

ERROR: MethodError: no method matching #solve#14(::Float64, ::Int64, ::Int64, ::Int64, ::Bool, ::typeof(solve), ::NeoclassicalGrowth, ::ValueCoeffs{IterateOnPolicy})

What completely tripped me up is that the arguments don’t come in the order the funciton is defined. I would have expected ncgm to come first. Why is that so or what I’m getting wrong here?

How exactly are you calling solve function so that it gives this particular error?

Please provide an MWE.

my problem is solved. there was a stupid typo dampen::Float64 = 1 which generated that weird error. The error message is a problem though. Here is how you can generate it.

  1. I downloaded this notebook and extracted all code from from it: QuantEcon – Notes
  2. I upgraded the code (0.6) to julia 1.1 by following suggestions of v0.7. in the process i must have introduced above mistake.
  3. here is my resulting file as a gist
  4. you can make the script work by setting dampen::Float64 = 1.0 on this line
  5. if you don’t make that change, the script returns this error:
julia> include("GrowthModelSolutionMethods_jl.jl")
ERROR: LoadError: MethodError: no method matching #solve#8(::Float64, ::Int64, ::Int64, ::Int64, ::Bool, ::typeof(solve), ::NeoclassicalGrowth, ::ValueCoeffs{IterateOnPolicy,Degree{2}})
Closest candidates are:
  #solve#8(::Float64, ::Int64, ::Float64, ::Int64, ::Bool, ::Any, ::NeoclassicalGrowth, ::ValueCoeffs{T<:SolutionMethod,D} where D<:Degree) where T<:SolutionMethod at /Users/74097/Dropbox/teaching/ScPo/ScPo-CompEcon/code/notebooks/GrowthModelSolutionMethods_jl.jl:254

where, again, it’s really totally confusing that the order of arguments is not identical to the function defintion. in that error message, the keyword arguments are referenced first!

Thanks for posting the script, but this is hardly a MWE :wink:

I think your problem can be boiled down to

julia> f(; x::Float64 = 1) = :works
f (generic function with 1 method)

julia> f()
ERROR: MethodError: no method matching #f#3(::Int64, ::typeof(f))
Closest candidates are:
  #f#3(::Float64, ::typeof(f)) at REPL[1]:1
Stacktrace:
 [1] f() at ./REPL[1]:1
 [2] top-level scope at REPL[1]:1

julia> f(; x = 1.0)
:works

ie you are overspecifying the keyword argument types for no good reason. Generally, this is not good programming style.

I am not sure how the error message could be improved (except for #solve#8, which can be cryptic). It describes what is happening concisely and accurately.

well, that’s not quite it. that’s more like it:

julia> f(y::Float64; x::Float64 = 1) = :works
f (generic function with 2 methods)

julia> f(1.0)
ERROR: MethodError: no method matching #f#30(::Int64, ::typeof(f), ::Float64)
Closest candidates are:
  #f#30(::Float64, ::Any, ::Float64) at REPL[7]:1

the error message could be improved dramatically by

  1. not saying that we want to match #f#30(::Int64, ::typeof(f), ::Float64)
  2. not coloring in red the first argument, Float64, which happens to be correct.

don’t you think?

It is colored in red in the candidate, so that part is correct.

As I said above, not using the internal form with the # in the error message would be an improvement. Cf

I’m not following. Here is how I think this should be:

g(x::Float64,y::Float64) = x
julia> g(1.0,2)
ERROR: MethodError: no method matching g(::Float64, ::Int64)
Closest candidates are:
  g(::Float64, ::Float64) at REPL[11]:100:

The red bit is of course the part that is wrong and needs to be changed - it says there is a g where the second argument (shown in red for your benefit) is a Float64, so supply the correct argument. Now compare that to the picture above. the first argument is red, when it should be the second.

The problem is not this, but that an internal implementation detail leaks into the error message.

Currently (on v"1.2.0-DEV.608")

f(x; y = z)

is translated to a call signature

g((y = z, ), f, x)

where you see g as a generated symbol name somehow related to f (currently something like #kw#f).

This is an internal detail, and might change at any point. The problem is not that things are not in the right order, or mismatched, but that this is printed at all, and not translated back to the original call form (for the error message).

This is known (see the above issue).

that’s good to know. i don’t care so much about that printing really. I think this inconsistency is really confusing. just for the record, i am talking about this inconsistency

julia> f(x::Float64;y::Int = 1.0) = x
f (generic function with 1 method)

julia> g(x::Float64,y::Int) = x
g (generic function with 1 method)

julia> f(0.0)
ERROR: MethodError: no method matching #f#5(::Float64, ::typeof(f), ::Float64)
Closest candidates are:
  #f#5(::Int64, ::Any, ::Float64) at REPL[1]:1
Stacktrace:
 [1] f(::Float64) at ./REPL[1]:1
 [2] top-level scope at none:0

julia> g(1.0,0.0)
ERROR: MethodError: no method matching g(::Float64, ::Float64)
Closest candidates are:
  g(::Float64, ::Int64) at REPL[2]:1
Stacktrace:
 [1] top-level scope at none:0

I think the first error message should not say that there is a candidate f with an Int as first argument available. Notice that calling the function with wrong kwarg behaves as I think it should:

julia> f(0.0,y = 1.0)
ERROR: TypeError: in keyword argument y, expected Int64, got Float64

anyway, i guess it won’t happen that often that someone messes up the default arg in their function definition like i did here.

But, if you read carefully, it doesn’t. It refers to #f#5, not f. The main issue is that you should not see #f#5 (because, again, it is just an implementation detail), but given that you do, the message is actually correct and consistent.