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

More helpful MethodErrors for deliberately unimplemented methods #7512

Open
ivarne opened this issue Jul 3, 2014 · 20 comments
Open

More helpful MethodErrors for deliberately unimplemented methods #7512

ivarne opened this issue Jul 3, 2014 · 20 comments
Labels
error handling Handling of exceptions by Julia or the user help wanted Indicates that a maintainer wants help on an issue or pull request

Comments

@ivarne
Copy link
Member

ivarne commented Jul 3, 2014

One of the first problems you stumble upon when learning Julia, is the MethodError. When using the standard library, the reason is usually that you are doing something wrong, but the only thing the users sees is ERROR: no method +(ASCIIString, ASCIIString). It would be very nice if the user would get a string with alternative suggestions.

Currently Exceptions are printed with the help of showerror(io::IO, err::MethodError) in replutil.jl

We could have something like a table for lookup:

deliberatly_unimplemented_methods = [
(:+, (String, String), "Use * for string concatenation"),
(:max, (AbstractArray,), "Use maximum to find the largest element in an array"),
]

, but maybe we can find a better solution that also captures varargs functions.

@samuelcolvin
Copy link
Contributor

gg discussion this came from

This makes a lot of sense. Could it also be used for depreciated functions? Right now I think they're still implemented but throw an exception or warning.

It should also be available for modules, so packages can point out the replacements for old functions.

In fact if all modules could implement deliberatly_unimplemented_methods someone could come along and write for example a "MatlabHelper" package that just defined lots of matlab functions that are not available in Julia with advice on equivalents.

@ivarne
Copy link
Member Author

ivarne commented Jul 3, 2014

Deprecated functions usually just prints a warning, and then perform what the old function was supposed to do. There might be something said for keeping a warning here, when the deprecation is fully removed though.

It should definitely be extensible for modules. Would it be enough to just document how to push! more suggestions onto the array?

@tpapp
Copy link
Contributor

tpapp commented Jul 3, 2014

IMO encountering minor differences in surface syntax or function names is entirely natural when learning a new language. I am not sure that hints like this belong in the language proper, especially since there are zillion other languages out there from which users can come to Julia, and it is nearly impossible to provide hints to everyone. For example, a former R user is likely to encounter difficulties different from someone coming from Python, etc.

Wiki pages which enumerate differences between Julia and another languages would be more useful IMO. Eg similar to the table at the end of http://norvig.com/python-lisp.html

@StefanKarpinski
Copy link
Member

+1 for this general approach.

@JeffBezanson
Copy link
Member

I love the idea of deliberately unimplemented methods. It would make the information from applicable and method_exists far more useful.

@ivarne
Copy link
Member Author

ivarne commented Jul 3, 2014

@tpapp It depends on how you look at it, but I would not consider replutils.jl part of the Julia language. Currently lots of tooling has found its way into the Base folder, and there is an open issue about cleaning it up. It is somewhat stuck because of technical limitations (system image) and a desire to have everybody use pretty much the same system when debugging issues.

Teaching language philosophy and differences is definitely not the target of this feature, but I don't think there are many functions where former R and Python users have different intentions when trying to call a undefined method in Julia.

It is also possible to save some memory by not lazy loading this list, so I don't think there are going to be problems when 10 languages add their entire standard library with suggestions for Julia alternaltives.

@simonster
Copy link
Member

I'm not sure this needs to be extensible from the start, but making this extensible seems like another case of #3988 (see also discussion in #5572).

@samuelcolvin
Copy link
Contributor

It should definitely be extensible for modules. Would it be enough to just document how to push! more suggestions onto the array?

Sounds fine to me.

@tpapp, I see what you mean, and I guess this is the difference between the purist and the popularist. Personally I want Julia to be popular and I think smoothing the initial "bump" of changing language is one of the most important ways of doing this.

I gave an introductory presentation on Julia the other day and one of the thinks people liked most was what they got "in the box", eg.;

  • Interactive REPL
  • Package manager
  • Support for parallelism

Strictly none of these are "part of the language" but it's convenient for them to come with it.

Remember, the biggest reason (by far) that people use matlab is the IDE that comes with the language, not the language.

@StefanKarpinski
Copy link
Member

Strictly none of these are "part of the language" but it's convenient for them to come with it.

One of the important things about shipping with something is that it tends to actually work, whereas add-ons have a much lower rate of working.

@Aerlinger
Copy link
Contributor

I really like the idea of having hints, but it seems a bit messy to define these values directly in the code. Perhaps there should be a fixture file (.method_hints?) that defines a list of these methods:

Each row of the .method_hints file would contain a tab-delimited signature of the method to relay the error:

For example:

:+      (String, String)     "Use * for string concatenation"
:max    (AbstractArray,)     "Use maximum to find the largest element in an array"

@ivarne
Copy link
Member Author

ivarne commented Jul 3, 2014

@Aerlinger That saves some typing, but I don't think a special purpose parser makes things less messy. It will also be a new syntax to learn (even though it is intuitive). Julia code can also be placed in a separate file and loaded on demand.

While we wait for someone to implement something like this, we should probably collect more examples on commonly missing methods. The format you use, can be treated as a vote.

@simonster
Copy link
Member

Per #7686, it would also be great to print "closest" matching methods for general MethodErrors, although defining the metric is going to be difficult.

@jhasse
Copy link
Contributor

jhasse commented Jul 21, 2014

This is how I think it would be ideal to display:

f(x::Float64, y::Float64) = println("ff")
f(x::Int, y::Int) = println("ii")
f(x::Float64, y::Int) = println("fi")

f(1, 1.0)
ERROR: `f` has no method matching f(::Int64, ::Float64)
Closest candidates are:
    f(::Float64, ::Float64)
      ^~~~~~~~~
    f(::Int, ::Int)
             ^~~~~
    f(::Float64, ::Int)
      ^~~~~~~~~  ^~~~~

These underlines should indicate which arguments don't match.

Even better would be if this utilizes colored output: The wrong types are printed red and the underlining ^~~~ can be removed.

@prcastro
Copy link
Contributor

I like @jhasse suggestion

@lindahua
Copy link
Contributor

I suspect that this might cause more daunting messages for methods that are heavily overloaded.

Consider:

type A end
A() + A()

This could be equally distant to all methods of +. Then this would produce a miles long list of things that virtually enumerate all 125 methods of + (probably many more if other packages are being used).

@jhasse
Copy link
Contributor

jhasse commented Jul 21, 2014

Maybe remove all candidates where no arguments match at all? E. g. in my previous example:

f(1, 1.0)
ERROR: `f` has no method matching f(::Int64, ::Float64)
Closest candidates are:
    f(::Float64, ::Float64)
      ^~~~~~~~~
    f(::Int, ::Int)
             ^~~~~

Then your example would result in no list, but A() + 0 would have one. This would still be rather long, but I think that is wanted isn't it? Or the list could be truncated after 5 elements or so.

@StefanKarpinski
Copy link
Member

That would never print suggestions for functions with fewer than two arguments – which might be ok. This is also pretty non-trivial to implement, but if you want to have a crack at it, please feel free. This would be awesome to have.

@ceving
Copy link

ceving commented Nov 30, 2016

Suggesting functions with different type signatures will not help in case of "a" + "b". Only the suggestion of a different function will help. I do not think that the correct suggestion can be found in general. A correct suggestion has to know, that the person has a Java background. But how about "a" . "b"? How to know, that this is a Perl guy? And how about 'a' || 'b'?

@ivarne
Copy link
Member Author

ivarne commented Nov 30, 2016

@ceving I don't understand what will not help.

The suggestion in this issue is to print helpful messages together with MethodError, when we see cases where we (think we) know what the user wants to do. The message might be anything, and will certainly often suggest different functions (actually both my examples suggest to change function (+ -> * and max -> maximum))

We know many programmers don't read the full manual before starting to program in Julia, and if we manage to save them of a few searches to figure out our api, that would save them a lot of time.

If a common language defines something similar to +(::String,::String) to mean something else, we can suggest the proper way to do that too.

@kshyatt kshyatt added the error handling Handling of exceptions by Julia or the user label Dec 1, 2016
mbauman added a commit that referenced this issue Oct 23, 2017
Fixes #7512.  This adds a hook for developers to add custom help messages when
`MethodError`s get displayed.  Feel free to bikeshed the naming of this method.

This is particularly helpful in cases of intentionally unimplemented methods.
I've also restructured the existing help messages in terms of this new system,
using the `info` command to display them:

```
julia> Int("1")
ERROR: MethodError: no method matching convert(::Type{Int64}, ::String)
INFO: Cannot `convert` an object of type String to an object of type Int64.
INFO: This may have arisen from a call to the constructor Int64(...), since type constructors fall back to convert methods.

Stacktrace:
 [1] Int64(::String) at ./sysimg.jl:114

julia> min([1,2,3])
ERROR: MethodError: no method matching min(::Array{Int64,1})
INFO: Use minimum() to find the smallest element of an array.

Closest candidates are:
  min(::AbstractArray{T1<:Real,N} where N, ::Real) where T1<:Real at deprecated.jl:55
  min(::AbstractArray{T1<:Real,N} where N, ::AbstractArray{T2<:Real,N} where N) where {T1<:Real, T2<:Real} at deprecated.jl:55
  min(::Any, ::Any) at operators.jl:404
  ...

julia> [1,2,3](1)
ERROR: MethodError: no method matching (::Array{Int64,1})(::Int64)
INFO: Use square brackets [] for indexing into arrays.
```

Feel free to bikeshed the name (`no_method_error_help`).
@shamsulalam1114
Copy link

Define the table for deliberately unimplemented methods and their suggestions

deliberately_unimplemented_methods = Dict(
:+ => ((String, String), "Use * for string concatenation"),
:max => ((AbstractArray,), "Use maximum to find the largest element in an array")
)

Extend the showerror function to use this table

function Base.showerror(io::IO, err::MethodError)
# Check if the method is deliberately unimplemented
for (method, (argtypes, suggestion)) in deliberately_unimplemented_methods
if err.f == method && err.types == argtypes
println(io, "MethodError: $(method)$(argtypes) is not implemented. $suggestion")
return
end
end
# Default behavior
Base.showerror(io, err)
end

Examples to trigger the MethodError

try
"Hello" + "World" # This will raise a MethodError
catch e
println(e)
end

try
max([1, 2, 3]) # This will raise a MethodError
catch e
println(e)
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
error handling Handling of exceptions by Julia or the user help wanted Indicates that a maintainer wants help on an issue or pull request
Projects
None yet
Development

Successfully merging a pull request may close this issue.