Default behavior from omitted catch block when a finally block is added.

223 views
Skip to first unread message

Harry Sungod

unread,
Apr 7, 2015, 2:54:18 PM4/7/15
to julia...@googlegroups.com
Hello All,

I find the following behavior a bit non-intuitive. IMHO, if addition of finally block changes the default behavior provided by the omitted catch block, we should force the user to declare a catch block.

# Version 0.4.0-dev+4160 (2015-04-06 03:40 UTC)  Commit 8fc5b4e* (1 day old master) x86_64-apple-darwin13.4.0

julia> function f1(x)
           try
               return x > 0 ? x/2 : error("x should be greater than zero")
           end # Let us assume user wants to ignore exception...
           println("returning normally without exception")
           -1
       end
f1 (generic function with 1 method)

julia> f1(1)
0.5

julia> f1(-1)
returning normally without exception
-1

julia> function f2(x)
           fp = open("/tmp/unrelated_resource.txt", "w")
           try
               return x > 0 ? x/2 : error("x should be greater than zero")
           finally
               close(fp) # only change is to close the file, no change intended for default on missing-catch-block
           end
           println("returning normally")
           -1
       end
f2 (generic function with 1 method)

julia> f2(1)
0.5

julia> f2(-1)
ERROR: x should be greater than zero
 in f2 at none:4

julia>

Thanks
--
Harry

Toivo Henningsson

unread,
Apr 7, 2015, 4:10:07 PM4/7/15
to julia...@googlegroups.com
I agree, seems only consistent that try without a catch should catch nothing.

ele...@gmail.com

unread,
Apr 7, 2015, 7:44:13 PM4/7/15
to julia...@googlegroups.com


On Wednesday, April 8, 2015 at 6:10:07 AM UTC+10, Toivo Henningsson wrote:
I agree, seems only consistent that try without a catch should catch nothing.

There are four combinations of the current syntax

1. try end
2. try catch end
3. try finally end
4. try catch finally end

but there are more than four possible behaviours when an exception occurs

1. propagate the exception
2. ignore the exception
3. catch the exception (then it doesn't exist)
4. propagate the exception running a finally
5. ignore the exception running a finally
6. catch the exception (then it doesn't exist) then run a finally

so some behaviours have to miss out :)

1. is pretty pointless inside a try block since it behaves as if its not there, so its not provided with a syntax, catch the exception and re-throw it if you really want to

5. is the other one to miss out on its own syntax, in general its probably the right decision because (empirically) its the least used and can be provided using try catch finally end

So the behaviours that do not have syntax are logical, but not symmetric with respect to finally, one is a propagate and the other is an ignore, and thats why the behaviour seems to be different, it is deliberately different.

Cheers
Lex


Toivo Henningsson

unread,
Apr 8, 2015, 1:08:53 AM4/8/15
to julia...@googlegroups.com

I think you are right that this is deliberate,  but I also think that making catch and finally orthogonal to each other should trump such case by case optimizations. It would allow a simpler mental model of the language. If you want the current behavior of

try end

you can use

try catch end

and I think there is something to be said for the explicitness that comes with only catching exceptions when there is a catch keyword involved.

I don't see how ignore in your list below is different from catching an exception and ignoring it. If you merge those two, your options reduce down to four, which map naturally to the four combinations of using our not using catch and finally respectively.

Harry Sungod

unread,
Apr 8, 2015, 2:28:15 AM4/8/15
to julia...@googlegroups.com
Lex,

I am fine not having the terse syntax of #1. In fact I am not asking why f2() threw an exception. I was only worried why the syntax of f1() lays a trap for me that I will fall in to later.

In other languages I am familiar with, "finally" is orthogonal to what happens inside catch (or whether it is caught at all). If you follow my example above, both f2() and f1() could have been the same code originally and a seemingly unrelated addition of finally changed the behavior of the rest of the code.

In terms of having a syntax for all combinations of outcomes - that wasn't my intent. I understand we couldn't possibly have a syntax for all combinations. For example, there can be exception that occurs in the finally block that masks the original exception. I see the behavior as just 'catch', 'rethrow or ignore', 'finally'.  Each of these then take its own path.

If we require a 'catch' block for every "try", it would be cleaner. Otherwise I suggest we had a note in the documentation to state the caveats. Particularly for those who come from other background and get too excited about "try end" terse syntax. I would be happy to make some edits in the documentation if you approve.

Thanks
--
Harry

ele...@gmail.com

unread,
Apr 8, 2015, 2:30:03 AM4/8/15
to julia...@googlegroups.com


On Wednesday, April 8, 2015 at 3:08:53 PM UTC+10, Toivo Henningsson wrote:

I think you are right that this is deliberate,  but I also think that making catch and finally orthogonal to each other should trump such case by case optimizations. It would allow a simpler mental model of the language. If you want the current behavior of

try end

you can use

try catch end

and I think there is something to be said for the explicitness that comes with only catching exceptions when there is a catch keyword involved.


The explicitness argument (rather than symmetry) is a good one, and seems to be the common solution with other exception languages.  An explicit empty 'catch` (or `except` in Python) to ignore exceptions, otherwise they propagate.
 

I don't see how ignore in your list below is different from catching an exception and ignoring it. If you merge those two, your options reduce down to four, which map naturally to the four combinations of using our not using catch and finally respectively.


Assuming no implementation differences, catch and do nothing is the same as ignore, yes.

You didn't say how they mapped, but I assume you replaced ignore with always propagate, ie without a catch or finally, a try end is useless, its just an expensive block :)  

Personally I'm ambivalent, but being explicit does seem to have been most languages choice du jour, so it may be worthwhile matching that to ease the load on programmers brains.

Mauro

unread,
Apr 8, 2015, 6:32:18 AM4/8/15
to julia...@googlegroups.com
> If we require a 'catch' block for every "try", it would be cleaner.
> Otherwise I suggest we had a note in the documentation to state the
> caveats. Particularly for those who come from other background and get too
> excited about "try end" terse syntax. I would be happy to make some edits
> in the documentation if you approve.

I read the docs after seeing your first post and was not any wiser. So
yes, some better docs would be good, thanks!

Jan van Oort

unread,
Apr 8, 2015, 8:45:36 AM4/8/15
to julia...@googlegroups.com
 there is something to be said for the explicitness that comes with only catching exceptions when there is a catch keyword involved.

Absolutely. Please go that way. It would be even better to make a "catch" mandatory with each "try", in order to ( as someone else here said ) ease the load on programmers' brains. 

Anything else would veer off into the exotic, IMHO. 




Op dinsdag 7 april 2015 20:54:18 UTC+2 schreef Harry B:

Harry B

unread,
Apr 8, 2015, 11:53:39 PM4/8/15
to julia...@googlegroups.com
I have put in a pull request for a documentation change related to this
https://github.com/JuliaLang/julia/pull/10776
Please note it contains a few more edits that seemed appropriate.

Please approve or let me know what needs to change.
I would be happy to amend the documentation again if you decide to
require catch clause based on suggestions here.

Thanks
--
Harry

Harry B

unread,
Apr 9, 2015, 8:16:29 PM4/9/15
to julia...@googlegroups.com

elextr, in case you didn't see my comment on the pull request, I am quoting it here

elextr, I am still purplexed by this comment

note the value nothing can be tested so you can tell if the block exited by exception rather than returning its last expression

julia> nothing == try println("hello") end
hello
true

If developers start using return of nothing to detect having got an exception, that is bad as well. In the above example, there is perfectly valid non-erroring block and it returns nothing (because println returns nothing)

IMHO, we should be simply saying 'it returns the last expression executed' (just like any other block) - that is how I have worded it.


ele...@gmail.com

unread,
Apr 9, 2015, 9:05:04 PM4/9/15
to julia...@googlegroups.com

On Friday, April 10, 2015 at 10:16:29 AM UTC+10, Harry B wrote:

elextr, in case you didn't see my comment on the pull request, I am quoting it here

elextr, I am still purplexed by this comment

note the value nothing can be tested so you can tell if the block exited by exception rather than returning its last expression

julia> nothing == try println("hello") end
hello
true

If developers start using return of nothing to detect having got an exception, that is bad as well. In the above example, there is perfectly valid non-erroring block and it returns nothing (because println returns nothing)

IMHO, we should be simply saying 'it returns the last expression executed' (just like any other block) - that is how I have worded it.

Replied on the PR, but to emphasise, *never* misdocument something because you think users "shouldn't" do it, thats a disservice to their intelligence, and causes much frustration and bug reports when it doesn't work the way you say.
Reply all
Reply to author
Forward
0 new messages