ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:110284] [Ruby master Misc#19054] `else` in exception-handling context vs early return
@ 2022-10-14 13:59 zverok (Victor Shepelev)
  2022-10-14 14:53 ` [ruby-core:110285] " jeremyevans0 (Jeremy Evans)
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: zverok (Victor Shepelev) @ 2022-10-14 13:59 UTC (permalink / raw
  To: ruby-core

Issue #19054 has been reported by zverok (Victor Shepelev).

----------------------------------------
Misc #19054: `else` in exception-handling context vs early return
https://bugs.ruby-lang.org/issues/19054

* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
----------------------------------------
`else` in exception-handling context is rarely used (at least in codebase I saw), so we encountered it just recently:
```ruby
def foo
  puts "body"
  return
rescue => e
  p e
else
  puts "else"
ensure
  puts "ensure"
end

foo # prints "body", then "ensure"

[1].each do
  puts "body"
  next
rescue => e
  p e
else
  puts "else"
ensure
  puts "ensure"
end
# also prints "body" then "ensure"
```
E.g. `else` is ignored in both cases. Intuitively, I would expect that if no exception is raised in block, `else` is performed always—like `ensure` is performed always, exception or not, early return or not.

I found only a very old discussion of this behavior in #4473 (it was broken accidentally on the road to 1.9.2, but then fixed back), but it doesn't explain the reason for it. 

Can somebody provide an insight on this decision, and whether it is justified at all?.. At least, it should be documented somewhere, [exception handling docs](https://docs.ruby-lang.org/en/master/syntax/exceptions_rdoc.html) doesn't mention this quirk.





-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [ruby-core:110285] [Ruby master Misc#19054] `else` in exception-handling context vs early return
  2022-10-14 13:59 [ruby-core:110284] [Ruby master Misc#19054] `else` in exception-handling context vs early return zverok (Victor Shepelev)
@ 2022-10-14 14:53 ` jeremyevans0 (Jeremy Evans)
  2022-10-14 16:14 ` [ruby-core:110286] " zverok (Victor Shepelev)
  2022-10-14 16:32 ` [ruby-core:110288] " jeremyevans0 (Jeremy Evans)
  2 siblings, 0 replies; 4+ messages in thread
From: jeremyevans0 (Jeremy Evans) @ 2022-10-14 14:53 UTC (permalink / raw
  To: ruby-core

Issue #19054 has been updated by jeremyevans0 (Jeremy Evans).


I think the existing behavior is expected.  `else` with `rescue` operates similar to `else` with `if`.  In pseudocode:

```ruby
begin
  if e = exception_raised_by{puts "body"; return}
    p e
  else
    puts "else"
  end
ensure
  puts "ensure"
end
```

I agree with you that the documentation could be improved, though it kind of hints at the current behavior:

* (Regarding `else`): "You may also run some code when an exception is not raised"
* "To always run some code whether an exception was raised or not, use ensure:"

It would be useful to document that else is not called on early exits or exceptions, and how to use `ensure` to run code on all non-exception scenarios (by using `rescue => local_var` and `if local_var`).

----------------------------------------
Misc #19054: `else` in exception-handling context vs early return
https://bugs.ruby-lang.org/issues/19054#change-99571

* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
----------------------------------------
`else` in exception-handling context is rarely used (at least in codebase I saw), so we encountered it just recently:
```ruby
def foo
  puts "body"
  return
rescue => e
  p e
else
  puts "else"
ensure
  puts "ensure"
end

foo # prints "body", then "ensure"

[1].each do
  puts "body"
  next
rescue => e
  p e
else
  puts "else"
ensure
  puts "ensure"
end
# also prints "body" then "ensure"
```
E.g. `else` is ignored in both cases. Intuitively, I would expect that if no exception is raised in block, `else` is performed always—like `ensure` is performed always, exception or not, early return or not.

I found only a very old discussion of this behavior in #4473 (it was broken accidentally on the road to 1.9.2, but then fixed back), but it doesn't explain the reason for it. 

Can somebody provide an insight on this decision, and whether it is justified at all?.. At least, it should be documented somewhere, [exception handling docs](https://docs.ruby-lang.org/en/master/syntax/exceptions_rdoc.html) doesn't mention this quirk.





-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [ruby-core:110286] [Ruby master Misc#19054] `else` in exception-handling context vs early return
  2022-10-14 13:59 [ruby-core:110284] [Ruby master Misc#19054] `else` in exception-handling context vs early return zverok (Victor Shepelev)
  2022-10-14 14:53 ` [ruby-core:110285] " jeremyevans0 (Jeremy Evans)
@ 2022-10-14 16:14 ` zverok (Victor Shepelev)
  2022-10-14 16:32 ` [ruby-core:110288] " jeremyevans0 (Jeremy Evans)
  2 siblings, 0 replies; 4+ messages in thread
From: zverok (Victor Shepelev) @ 2022-10-14 16:14 UTC (permalink / raw
  To: ruby-core

Issue #19054 has been updated by zverok (Victor Shepelev).


Well, my pseudo-code-expressed intuition can be rather expressed like this:
```ruby
begin
  # whatever happens here, is covered by rescue/else/ensure block
  e = exception_raised_by{puts "body"; return}
ensure
  if e
    # we go here if there was an exception
    p e
  else
    # we go here if there was none
    puts "else"
  end
  # we go here in any case
  puts "ensure"
end
```
It is implied by `else` and `rescue` being on the same level as `ensure`, making me think there are 3 blocks of equal priority
* one that performs always (`ensure`)
* one that performs if there was exception ([one of the] `rescue`)
* one that performs if there was no exception (`else`)

If we'll imagine more realistic code, there can be, like, 30 lines of methods body, and overall structure on reading looking like this:
```ruby
def my_method
  # a LOT can go here, 
  # ...but while reading
  # ...to the very end
  # ...you can always rely
  # ...on the fact that
  # even if THIS last line is not performed due to some reason,
rescue
  # this WILL perform if any exception happens, however deep it was
else
  # this WILL perform if no exception happened, no matter what
ensure
  # this WILL perform no matter what, period
end
```

Again, for me it somewhat theoretical, but I can imagine good uses for `else` like, at the very least, `log.debug 'success'`. With current behavior, it seems completely redundant feature, because imagine this:
```ruby
def my_method
  return :early

  puts "(1) printed at the end in normal, no early return-flow"
  :regular
rescue
  # ...
else
  puts "I would hope this will print for both :early, and :regular, but it behaves JUST like (1)"
end
```
...e.g. NOTHING is achieved by `else` that can't be achieved by putting exactly the same code at the end of the body.

----------------------------------------
Misc #19054: `else` in exception-handling context vs early return
https://bugs.ruby-lang.org/issues/19054#change-99572

* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
----------------------------------------
`else` in exception-handling context is rarely used (at least in codebase I saw), so we encountered it just recently:
```ruby
def foo
  puts "body"
  return
rescue => e
  p e
else
  puts "else"
ensure
  puts "ensure"
end

foo # prints "body", then "ensure"

[1].each do
  puts "body"
  next
rescue => e
  p e
else
  puts "else"
ensure
  puts "ensure"
end
# also prints "body" then "ensure"
```
E.g. `else` is ignored in both cases. Intuitively, I would expect that if no exception is raised in block, `else` is performed always—like `ensure` is performed always, exception or not, early return or not.

I found only a very old discussion of this behavior in #4473 (it was broken accidentally on the road to 1.9.2, but then fixed back), but it doesn't explain the reason for it. 

Can somebody provide an insight on this decision, and whether it is justified at all?.. At least, it should be documented somewhere, [exception handling docs](https://docs.ruby-lang.org/en/master/syntax/exceptions_rdoc.html) doesn't mention this quirk.





-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [ruby-core:110288] [Ruby master Misc#19054] `else` in exception-handling context vs early return
  2022-10-14 13:59 [ruby-core:110284] [Ruby master Misc#19054] `else` in exception-handling context vs early return zverok (Victor Shepelev)
  2022-10-14 14:53 ` [ruby-core:110285] " jeremyevans0 (Jeremy Evans)
  2022-10-14 16:14 ` [ruby-core:110286] " zverok (Victor Shepelev)
@ 2022-10-14 16:32 ` jeremyevans0 (Jeremy Evans)
  2 siblings, 0 replies; 4+ messages in thread
From: jeremyevans0 (Jeremy Evans) @ 2022-10-14 16:32 UTC (permalink / raw
  To: ruby-core

Issue #19054 has been updated by jeremyevans0 (Jeremy Evans).


zverok (Victor Shepelev) wrote in #note-2:
> ...e.g. NOTHING is achieved by `else` that can't be achieved by putting exactly the same code at the end of the body.

Incorrect.  The primary reason for `else` with `rescue` is that code in `else` that raises exceptions does not call into the `rescue` blocks (though it is still handled by `ensure`).  Code that the end of the body obviously would.

In any case, I would guess there is no chance of changing the behavior at this point.  It would break way too much code.


----------------------------------------
Misc #19054: `else` in exception-handling context vs early return
https://bugs.ruby-lang.org/issues/19054#change-99573

* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
----------------------------------------
`else` in exception-handling context is rarely used (at least in codebase I saw), so we encountered it just recently:
```ruby
def foo
  puts "body"
  return
rescue => e
  p e
else
  puts "else"
ensure
  puts "ensure"
end

foo # prints "body", then "ensure"

[1].each do
  puts "body"
  next
rescue => e
  p e
else
  puts "else"
ensure
  puts "ensure"
end
# also prints "body" then "ensure"
```
E.g. `else` is ignored in both cases. Intuitively, I would expect that if no exception is raised in block, `else` is performed always—like `ensure` is performed always, exception or not, early return or not.

I found only a very old discussion of this behavior in #4473 (it was broken accidentally on the road to 1.9.2, but then fixed back), but it doesn't explain the reason for it. 

Can somebody provide an insight on this decision, and whether it is justified at all?.. At least, it should be documented somewhere, [exception handling docs](https://docs.ruby-lang.org/en/master/syntax/exceptions_rdoc.html) doesn't mention this quirk.





-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2022-10-14 16:32 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-10-14 13:59 [ruby-core:110284] [Ruby master Misc#19054] `else` in exception-handling context vs early return zverok (Victor Shepelev)
2022-10-14 14:53 ` [ruby-core:110285] " jeremyevans0 (Jeremy Evans)
2022-10-14 16:14 ` [ruby-core:110286] " zverok (Victor Shepelev)
2022-10-14 16:32 ` [ruby-core:110288] " jeremyevans0 (Jeremy Evans)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).