This is when I got 2 colons for types mixed up with what I thought might be a default of “1” for the
middle value in StepRanges:
julia> for i in 1::4
println(i)
end
ERROR: TypeError: in typeassert, expected Type, got a value of type Int64
Stacktrace:
[1] top-level scope
@ ./REPL[1]:1
not particularly helpful. Might be helpful if it gave two or three or four options:
the one shown (The double colon is correct, the 4 is wrong)
I’m not sure Julia can handle this any better than it does, since ::is valid syntax for a type-assert. Julia understands your code along the lines of this snippet:
julia> for i in 1::Int64
println(i)
end
1
The problem is that instead of Int64, you had 4, which is an integer instead of the expected Type. So the error message is appropriate. Obviously, you meant:4, but Julia can’t exactly read your mind. A good linter might be able to catch this with a better heuristic, and give you a better error. Probably, the linter would catch on to for i in 1, with 1 not being something iterable, which is almost certainly not something you intend.
P.S.: Thanks for figuring out how to format your posts!
Well, I think this confirms for me what at least part of the problem is, and that is: The Julia error messages are written as if they are one expert communicating with another expert, instead of communicating with a novice, (Which everyone who doesn’t change their message level is
saying they are a novice). So, we have what I think are some truths:
Error messages are a place for simplicity, not for jargon (if the error message explained typeassert, that would have been helpful, but it assumes I already know the jargon; could have said … in typeassert (started by the :: operator) …)
Julia knows a lot more information than it is printing out (for example, the arg types to functions are often printed, but not the names). Printing out the extra information, while not useful to the gurus, could be a huge win for the novices, and not much effort in the big scheme of things.
Could you clarify this because argument names are printed in stacktraces:
julia> foo(x) = bar(x);
julia> bar(y) = y+1;
julia> foo("one")
ERROR: MethodError: no method matching +(::String, ::Int64)
The function `+` exists, but no method is defined for this combination of argument types.
...
Stacktrace:
[1] bar(y::String)
@ Main .\REPL[2]:1
[2] foo(x::String)
@ Main .\REPL[1]:1
[3] top-level scope
@ REPL[3]:1
I think the author is referencing another thread where MethodError’s “closest candidates” list does not show field names:
julia> struct TEST_STRUCT; A; B end
julia> TEST_STRUCT(1)
ERROR: MethodError: no method matching TEST_STRUCT(::Int64)
The type `TEST_STRUCT` exists, but no method is defined for this combination of argument types when trying to construct it.
Closest candidates are:
TEST_STRUCT(::Any, ::Any)
@ Main REPL[131]:1
Stacktrace:
[1] top-level scope
@ REPL[132]:1
julia> methods(TEST_STRUCT) # me checking that default constructors have field names
# 1 method for type constructor:
[1] TEST_STRUCT(A, B)
@ REPL[131]:1
There is actually (experimental) functionality for adding more detailed error messages. It could be an idea to have a module which adds such messages. It could be loaded with e.g. using ErrorMessageSuggestions. This is how it works:
function Base.Experimental.show_error_hints(io, ex::TypeError)
if ex.func == :typeassert
println(io, """\n
- The object following :: is not a type
- Or, the `::` should have been a `:`
""")
end
end
julia> for i in 1::4
println(i)
end
ERROR: TypeError: in typeassert, expected Type, got a value of type Int64
- The object following :: is not a type
- Or, the `::` should have been a `:`
However, it all depends on where the TypeError was thrown. There is a field in the TypeError struct for adding a textual context. Some more explanation could have been inserted there.
Looks like sometimes they are, and sometimes not, from earlier code:
ERROR: MethodError: no method matching Son(::Int64)
The type `Son` exists, but no method is defined for this combination of argument types when trying to construct it.
Closest candidates are:
Son(::Any, ::Any)
@ Main REPL[23]:1
Stacktrace:
[1] top-level scope
@ REPL[24]:1
so, to clarify, it would be nice to always print the arg names and types.
It’s not possible to know the argument names if the methods aren’t known, it’s the same reason for MethodError: no method matching +(::String, ::Int64). It might be possible to adding argument names to the methods listed “Closest candidates”, but that wouldn’t be necessary to fix input types. The argument names aren’t useful for the call site or knowing how the methods work anyway, we use the line numbers to follow those.
Closest candidates are:
Son(::Any, ::Any)
@ Main REPL[23]:1
That’s what keeps getting overlooked: the argument names aren’t useful to you, knowing how…
Correct, they are not useful to you — I am saying that I think they would be useful to non-gurus
who are looking for every scrap of info they can find.
The problem with error messages is that many of them are not due to syntax errors, but inherently runtime phenomena.
Consider a function like:
function f(a, b)
for i in a::b
println(i)
end
end
The call f(1, 4) fails, whereas f(1:4, UnitRange{Int}) succeeds. f(eachindex(rand(4)), Base.OneTo{Int}) also succeeds, as well as f(3:0, OrdinalRange) and f(1,Int), but f(1:10, StepRange) fails.
This is of course an artificial example, but it illustrates a problem with catching errors in a meaningful way when types are determined at runtime.
It’s simply quite hard to give good runtime error texts when functions have been compiled, and the actual syntax which was in question no longer is easily available.
julia> abstract type AbstractParentType <: Any end
julia> struct Son AbstractParentType
field1
end
julia> Son(44)
ERROR: MethodError: no method matching Son(::Int64)
The type `Son` exists, but no method is defined for this combination of argument types when trying to construct it.
Closest candidates are:
Son(::Any, ::Any)
@ Main REPL[11]:1
Stacktrace:
[1] top-level scope
@ REPL[12]:1
julia> "In this case, if arg names were printed, the line after Closest candidates are would read:" ;
julia> "Son(AbstractParentType::Any,field1::Any";
julia> "which would be **huge**, telling me that julia thinks that AbstractParentType is a field name,";
julia> "wheras I am thinking it is a type name; fixing that then fixes the problem";
julia>
I suppose the first thing to do is to look at what methods exists?
julia> methods(Son)
# 1 method for type constructor:
[1] Son(AbstractParentType, field1)
Argument names are usually printed in stack traces, and in print of method tables as above, I don’t know why they are not included in the “Closest candidates” output.
Agree with everything you said, FWIW.
Just means I get to dazzle you with my terrific hindsight:
It is difficult or impossible to give good runtime error texts, so it needs to be “designed in”
to the language from the start.
So in this case it would help because the struct definition (and also metaprogramming) generated methods, so it’s not visibly clear what went wrong in those methods. Again, we’d normally follow the stack trace’s line number to the struct definition, but that doesn’t help when you already misunderstood what the struct definition did. As sgaure commented, reflection can help, but so would putting more of the method signature into the Closest Candidates printout.
Maybe for brevity? In other failed dispatch cases where there isn’t a misunderstanding of what methods were defined, this printout is intended for finding method ambiguities or mistaken input types for the call, in which case method argument names really are useless. But I personally wouldn’t be bothered by the extra information of a full method signature, and evidently it can save a few clicks and some time in narrow circumstances.
Not sure what you mean by “start” here, but runtime error messages can be modified over Julia versions because it doesn’t break code. Making the changes is just a matter of making a good argument in a Github issue, submitting a fix in a pull request, and convincing enough developers to merge it into newer patches and minor versions.