Expected 'end' message not the most helpful in throw function

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 ().

Sure. You can write things like:

if sthng
    throw
    Whatever()
end

If you forget the syntax of some other language, you won’t necessarily get meaningful error messages there either.

2 Likes

The font shouldn’t be an issue here, or different. Could you post a screen shot showing what you’re seeing?

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.

No problem. I try to keep an eye on potential font issues…

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
3 Likes

A couple of days ago I had to write a python script, something I seldom do. And I thought raise was a function:

>>> help(raise)
  File "<stdin>", line 1
    help(raise)
         ^^^^^
SyntaxError: invalid syntax

There is no shortage of weird error messages in this world.

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.

1 Like

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.

1 Like

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

You’re missing a lot of backticks there for readability.

The latter clause isn’t true as stated, what do you mean? Maybe the bad formatting is covering up an explanation.

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:

julia> (i::Integer)(x) = println("$i+$x=",i+x)
julia> a = 14
14
julia> a(28)
14+28=42

And in this example, the function f sometimes throws, sometimes does nothing, and sometimes prints the text "boo!":

function f()
    if rand(Bool)
        a = 1
    end
    if rand(Bool)
        a
        println("boo!")
    end
end

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.)

1 Like

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.