Issue #15567 has been updated by ioquatix (Samuel Williams).


@shevegen - thanks for your feedback.

> The reason why I dislike it is partially due to the syntax verbosity

With regards to syntax, it should use existing keywords, otherwise it can cause more problems. The reason for `not` is already explained, because otherwise user may have to enumerate all other tags which is inconvenient and may break if new tags are introduced in the future. If you can propose better syntax, please do.

>  but also because, more significantly, because it appears to do conditional checks in very different means than have existed before in ruby.

This isn't really a conditional check like `if`, and it's not even a big change internally, it simply exposes a bit more of the exception handling mechanisms. It should be more efficient, because `ensure when not return` can be handled as part of exceptional flow control.

It's more similar to `rescue` than `ensure` in it's overhead. In MRI, `rescue` has some overhead, but it's possible to implement zero-cost exception handling.

The reason why I think this is a good idea is because Ruby provides a lot of flow control statements, but handling these abnormal flow control is very tricky and easy to get wrong. Statements like `ensure ... unless $!` is actually incorrect as well as inefficient. `rescue Exception` doesn't handle all ways a function can exit.

I think the key things to think about would be:

- Is there a better syntax for this proposal?
- Does introducing such feature have any practical downside?
  - Implementation cost.
  - Maintenance cost.
  - Does it introduce technical debt?
- Does it solve a problem that can't be solved some other way?
  - `ensure when not return` can be partly solved some other way but with some overhead.
  - `ensure when raise` is similar to `rescue Exception` but doesn't require `raise` to resume default exception handling.
  - `ensure when retry` and other formulations (`throw`, `return`, `next`, `break`, `redo`, `retry`) have no equivalent right now.
  - `ensure when ..., ..., ...` is practically impossible, each case needs to be handled independently if possible.
- Even if there are existing solutions, does this proposed solution communicate more clearly the intention of the programmer?
- Do we want to expose this level of behaviour to user code?
- Could we limit what tags you can match? I suspect the most important one would be `ensure when not return`, but I can also imagine it could be pretty useful to catch things like `retry` and `redo`.


----------------------------------------
Feature #15567: Allow ensure to match specific situations
https://bugs.ruby-lang.org/issues/15567#change-76540

* Author: ioquatix (Samuel Williams)
* Status: Open
* Priority: Normal
* Assignee: ioquatix (Samuel Williams)
* Target version: 
----------------------------------------
There are some situations where `rescue Exception` or `ensure` are not sufficient to correctly, efficiently and easily handle abnormal flow control.

Take the following program for example:

```
def doot
	yield
ensure
	# Did the function run to completion?
	return "abnormal" if $!
end

puts doot{throw :foo}
puts doot{raise "Boom"}
puts doot{"Hello World"}

catch(:foo) do
	puts doot{throw :foo}
end
```

Using `rescue Exception` is not sufficient as it is not invoked by `throw`.

Using `ensure` is inefficient because it's triggered every time, even though exceptional case might never happen or happen very infrequently.

I propose some way to limit the scope of the ensure block:

```
def doot
	yield
ensure when raise, throw
	return "abnormal"
end
```

The scope should be one (or more) of `raise`, `throw`, `return`, `next`, `break`, `redo`, `retry` (everything in `enum ruby_tag_type` except all except for `RUBY_TAG_FATAL`).

Additionally, it might be nice to support the inverted pattern, i.e.

```
def doot
	yield
ensure when not return
	return "abnormal"
end
```

Inverted patterns allow user to specify the behaviour without having problems if future scopes are introduced.

`return` in this case matches both explicit and implicit.




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

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>