[#116016] [Ruby master Bug#20150] Memory leak in grapheme clusters — "peterzhu2118 (Peter Zhu) via ruby-core" <ruby-core@...>
Issue #20150 has been reported by peterzhu2118 (Peter Zhu).
7 messages
2024/01/04
[#116382] [Ruby master Feature#20205] Enable `frozen_string_literal` by default — "byroot (Jean Boussier) via ruby-core" <ruby-core@...>
Issue #20205 has been reported by byroot (Jean Boussier).
77 messages
2024/01/23
[ruby-core:116077] [Ruby master Feature#20160] rescue keyword for case expressions
From:
"austin (Austin Ziegler) via ruby-core" <ruby-core@...>
Date:
2024-01-08 16:18:28 UTC
List:
ruby-core #116077
Issue #20160 has been updated by austin (Austin Ziegler).
lloeki (Loic Nageleisen) wrote in #note-7:
> > If #parse is defined as:
>
> This requires:
>
> a) parse to be in your control
It does not. A parse wrapper *could* be written, just as I did with `get` in the URI example.
> b) parse to handle every possible exception (including whatever it calls) for which one would want a rescuing clause to control flow.
Not at all. `rescue => error` and returning `[:error, error]` is sufficient to handle anything that `parse` *does not* already handle.
> That is the point: exceptions are a first class Ruby concept. To me it feels off to create wrappers (e.g with this new get method or the wrapper above), munge return values with in band metadata (this [:error, exception] return value), or split control flow in two parts (begin rescue followed by case when) when logically it is one control flow, this is why I felt there may be interest in such a proposal.
Exceptions are part of Ruby, but exception *handlers* are not cheap.
> I do agree that pattern matching feels as much of a good potential candidate as any for such an exception rescuing feature. A specific pattern syntax could be used to match a given exception, and `else` would make sense.
`else` still does not make sense as Kevin Newton said in [#5](https://bugs.ruby-lang.org/issues/20160#note-5), as the `begin/else/end` is a wholly different context than `case/when/else/end` or `case/in/else/end`. The former is for when no exception is thrown; the latter two are when there is no other match made. I don't see a way to reconcile that particular conceptual roadblock *aside* from your parser function returning something more meaningful.
Exceptions aren't really supposed to be used for flow control, which is sort of what you're doing here. `throw/catch` is more related to flow control than exceptions, IMO.
I think that what you've presented here is *interesting*, but I could not see using it.
----------------------------------------
Feature #20160: rescue keyword for case expressions
https://bugs.ruby-lang.org/issues/20160#change-106074
* Author: lloeki (Loic Nageleisen)
* Status: Open
* Priority: Normal
----------------------------------------
It is frequent to find this piece of hypothetical Ruby code:
```
case (parsed = parse(input))
when Integer then handle_int(parsed)
when Float then handle_float(parsed)
end
```
What if we need to handle `parse` raising a hypothetical `ParseError`? Currently this can be done in two ways.
Either option A, wrapping `case .. end`:
```
begin
case (parsed = parse(input))
when Integer then handle_int(parsed)
when Float then handle_float(parsed)
# ...
end
rescue ParseError
# ...
end
```
Or option B, guarding before `case`:
```
begin
parsed = parse(input)
rescue ParseError
# ...
end
case parsed
when Integer then handle_int(parsed)
when Float then handle_float(parsed)
# ...
end
```
The difference between option A and option B is that:
- option A `rescue` is not localised to parsing and also covers code following `when` (including calling `===`), `then`, and `else`, which may or may not be what one wants.
- option B `rescue` is localised to parsing but moves the definition of the variable (`parsed`) and the call to what is actually done (`parse(input)`) far away from `case`.
With option B in some cases the variable needs to be introduced even though it might not be needed in `then` parts (e.g if the call in `case` is side-effectful or its value simply leading to branching decision logic).
The difference becomes important when rescued exceptions are more general (e.g `Errno` stuff, `ArgumentError`, etc..), as well as when we consider `ensure` and `else`. I feel like option B is the most sensible one in general, but it adds a lot of noise and splits the logic in two parts.
I would like to suggest a new syntax:
```
case (parsed = parse(input))
when Integer then handle_int(parsed)
when Float then handle_float(parsed)
rescue ParseError
# ...
rescue ArgumentError
# ...
else
# ... fallthrough for all rescue and when cases
ensure
# ... called always
end
```
If more readability is needed as to what these `rescue` are aimed to handle - being more explicit that this is option B - one could optionally write like this:
```
case (parsed = parse(input))
rescue ParseError
# ...
rescue ArgumentError
# ...
when Integer then handle_int(parsed)
when Float then handle_float(parsed)
...
else
# ...
ensure
# ...
end
```
Keyword `ensure` could also be used without `rescue` in assignment contexts:
```
foo = case bar.perform
when A then 1
when B then 2
ensure bar.done!
end
```
Examples:
- A made-up pubsub streaming parser with internal state, abstracting away reading from source:
```
parser = Parser.new(io)
loop do
case parser.parse # blocks for reading io in chunks
rescue StandardError => e
if parser.can_recover?(e)
# tolerate failure, ignore
next
else
emit_fail(e)
break
end
when :integer
emit_integer(parser.last)
when :float
emit_float(parser.last)
when :done
# e.g EOF reached, IO closed, YAML --- end of doc, XML top-level closed, whatever makes sense
emit_done
break
else
parser.rollback # e.g rewinds io, we may not have enough data
ensure
parser.checkpoint # e.g saves io position for rollback
end
end
```
- Network handling, extrapolated from [ruby docs](https://ruby-doc.org/stdlib-2.7.1/libdoc/net/http/rdoc/Net/HTTP.html#class-Net::HTTP-label-Following+Redirection):
```
case (response = Net::HTTP.get_response(URI(uri_str))
rescue URI::InvalidURIError
# handle URI errors
rescue SocketError
# handle socket errors
rescue
# other general errors
when Net::HTTPSuccess
response
when Net::HTTPRedirection then
location = response['location']
warn "redirected to #{location}"
fetch(location, limit - 1)
else
response.value
ensure
@counter += 1
end
```
Credit: the idea initially came to me from [this article](https://inside.java/2023/12/15/switch-case-effect/), and thinking how it could apply to Ruby.
--
https://bugs.ruby-lang.org/
______________________________________________
ruby-core mailing list -- ruby-core@ml.ruby-lang.org
To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/