If you forget that “throw” in Julia is a function (as I probably always will) expect to get this message, which I found to be misleading. How about Function "throw" needs a left parenthesis
for j in 1:1
throw DomainError()
ERROR: ParseError:
# Error @ REPL[2]:2:8
for j in 1:1
throw DomainError()
# └ ── Expected `end`
Stacktrace:
[1] top-level scope
@ REPL:1
in the fixed width courier font (my terminal) the vertical upper half pipe is between the w in “throw”
and the D in DomainError. shows up different in this font, I tried to move it to the original spot.
In other words, it seems to think the “throw” is correct, but somehow not figure out the ().
I thought I needed to edit the output because the vertical bar was in the wrong spot. I thought I was fixing it, but was making it wrong; I edited it after post, it is correct now, as it always was
unless you saw it the first 8 seconds it was up.
The proposed error is actually impossible because throw is not a reserved keyword, just a name in the module Core. For example, this is valid:
julia> let throw = 1
throw + 2
end
3
So the parser actually has no idea what throw does, let alone that it must be a callable function. This is generally true for languages with assignments and function definitions in namespaces.
The one exception I know is Python 3's print function, and that's a special case because Python 2 reserved a leading spaced keyword for it; otherwise, spaced out names just get a generic syntax error. That error becomes misleading in scopes where print gets reassigned (expand for example).
>>> print = 1
>>> print 2
File "<python-input-1>", line 1
print 2
^^^^^^^
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?
>>> print(2)
Traceback (most recent call last):
File "<python-input-2>", line 1, in <module>
print(2)
~~~~~^^^
TypeError: 'int' object is not callable
>>> print + 2
3
There’s a difference between Scheme and Common Lisp here. Julia is scheme-derived, in that functions and objects live in the same name table. R is Common Lisp derived, in that they live in separate name tables, so there can be a variable throw which is different from the function throw, in the same scope.
While @Benny is of course correct that this is just some syntax error and the precise mistake of the user cannot be inferred in general, I still wonder whether we could introduce more hints into the parser. Like in this scenario: if throw is used like this, we could print the syntax error and then an additional line like “it looks like you want raise an exception. If that is the case the correct syntax is <…>”.
Over time we could accumulate these hints and I think that this would make Julia more newbie-friednly overall. Is this something that could be done in a package?
Aside: The technical terms are: Scheme is a so-called Lisp-1 while Common Lisp is a Lisp-2.
I don’t know how the Julia parser is set up, but it’s possible for a parser to do this, Python 3’s does for print as demonstrated earlier. If “newbie-friendliness” is the goal though, it’d be worth making that extra warning very conditional on 1) the intent to throw an error, 2) throw === Core.throw in that scope, 3) the following expression evaluating to a valid input error. If any of those aren’t true, the incorrect inference of (1) would just be frustrating, and the parentheses suggestion would just result in more bugs as also demonstrated by Python 3’s print earlier.
I have totally forgotten about that, don’t think I’ve ever run into it either. I’ll try to make an example for fun later.
Something seems “off” here. “throw” advertises itself as a built-in function, but does not require parentheses like a function. Yes, it is more deeply broken than I had expected, not only does it work with “throw” there , but it works with other functions also. There is no throw being done by the way, and no return, no break,no continue, no nothing, just isgnoes the word throw I guess.
**julia>** throw
throw (built-in function)
Back to the original code …close to it …
ERROR: ArgumentError: throw: too few arguments (expected 1)
Stacktrace:
[1] top-level scope
@ REPL[5]:1
the word "sum" works instead of the word throw also:
julia> if sthing
sum
Whatever(); :noThrow
else
:noThrowHereEither
end
:noThrow
julia> sthing = false
false
julia> if sthing
sum
Whatever(); :noThrow
else
:noThrowHereEither
end
:noThrowHereEither
Perhaps it’s your understanding of the language which is deeply broken?
This is perfectly legal:
function f(b)
if b
throw
else
println
end
end
julia> f1 = f(true)
throw (built-in function)
julia> f2 = f(false)
println (generic function with 5 methods)
julia> f1(ArgumentError("whatever"))
ERROR: ArgumentError: whatever
Stacktrace:
[1] top-level scope
@ REPL[20]:1
julia> f2(ArgumentError("whatever"))
ArgumentError("whatever")
In this example, f1 will be the function throw, whereas f2 is the function println. So the effect of the f1 call is to throw an error, whereas the result of the f2 call is just the printed exception, which has not been thrown.
It would surely be weird to have the parser barf on the definition of f, just because somebody might happen to be under the impression that throw should be a keyword rather than a function.
Things like this also work. Functions are just ordinary objects which can be passed around, and called whenever you please:
function f()
rand( (+, -, *, ÷) )
end
julia> f()(2,3)
0
julia> f()(2,3)
6
julia> f()(2,3)
-1
You may even make other objects callable, like integers, but I strongly discourage to do this with types you haven’t made yourself:
None of those conditions are things the parser can know at that stage though, right? We can just make it a general tip like “Hint: To throw an exception, use throw(YourExceptionType())”. That doesn’t assume anything about the user code, just gives a tip that will be useful to beginners and can be ignored by expert users who know that they have fiddled with what throw means in this scope.
I’m more bothered by the ad-hoc nature of it, just special casing it like this, but again I don’t think there’s enough information available to the parser to do anything else. (Hypothetically, JuliaSyntax could maybe pass this error on to JuliaLowering in the future, and JuliaLowering could give a better error message with the much improved context about the code it has access to, but we’re deep in speculation territory there.)
I’m saying that throw(YourExceptionType()) only works if those conditions are met, so any hint should make that clear. A misleading hint is arguably worse than a vague error, and being precise is exactly what beginners need. That’s also as much of an argument to not special-case the error and expect even beginners to read the Manual section or a better tutorial about Exception Handling. It’s not possible for anything, let alone an error message, to anticipate every typo someone may make and perfectly guess what they meant based on their unknown background knowledge.