[ruby-core:121184] [Ruby master Feature#21160] Local return from proc

Issue #21160 has been reported by JustJosh (Joshua Stowers). ---------------------------------------- Feature #21160: Local return from proc https://bugs.ruby-lang.org/issues/21160 * Author: JustJosh (Joshua Stowers) * Status: Open ---------------------------------------- When writing DSL-style helper methods, I often store block arguments as procs to use as callbacks. Using `return` in a proc will return from the context it was created in, which is unsuitable in the following example. Since procs cannot be converted to lambdas, I end up using `next` to return a value from them early. Example: ``` ruby fulfills_promise :generate_large_image do |image_data| next false if image_data.nil? puts 'Saving image..' # etc. end ``` This works but confuses most readers. I propose introducing an alias for it that is more appropriate for this use case. Perhaps `pass` or `continue`? It's worth noting that `return` would work with `fulfills_promise :foo, -> (bar) do`, though it detracts a bit from a DSL's expressiveness. -- https://bugs.ruby-lang.org/

Issue #21160 has been updated by ufuk (Ufuk Kayserilioglu). `next` isn't necessarily the correct thing to use here, `break` is: ```ruby foo = fulfills_promise :generate_large_image do |image_data| break false if image_data.nil? puts 'Saving image..' # etc. end foo #=> false ``` And the name exactly conveys the concept of breaking out of the block, in my opinion. For example: ```ruby result = (1..100).each do |num| break num if num > 3 puts num end puts "Got result #{result}" ``` will print ``` 1 2 3 Got result 4 ---------------------------------------- Feature #21160: Local return from proc https://bugs.ruby-lang.org/issues/21160#change-112120 * Author: JustJosh (Joshua Stowers) * Status: Open ---------------------------------------- When writing DSL-style helper methods, I often store block arguments as procs to use as callbacks. Using `return` in a proc will return from the context it was created in, which is unsuitable in the following example. Since procs cannot be converted to lambdas, I end up using `next` to return a value from them early. Example: ``` ruby fulfills_promise :generate_large_image do |image_data| next false if image_data.nil? puts 'Saving image..' # etc. end ``` This works but confuses most readers. I propose introducing an alias for it that is more appropriate for this use case. Perhaps `pass` or `continue`? It's worth noting that `return` would work with `fulfills_promise :foo, -> (bar) do`, though it detracts a bit from a DSL's expressiveness. -- https://bugs.ruby-lang.org/

Issue #21160 has been updated by JustJosh (Joshua Stowers). ufuk (Ufuk Kayserilioglu) wrote in #note-1:
`next` isn't necessarily the correct thing to use here, `break` is:
I've tried `break` without success - I get `LocalJumpError: break from proc-closure` Here is a more complete example of what I am doing: ```ruby class Example def self.fulfills_promise(promise_name, &block) @@promise_callbacks ||= {} @@promise_callbacks[promise_name] = block end def self.fulfill_promise(promise_name, data) puts "Fulfilling promise: #{promise_name}" callback = @@promise_callbacks[promise_name] if callback.call(data) puts 'Complete!' else puts 'Failed!' end end fulfills_promise :generate_large_image do |image_data| puts 'Will finalize large image...' break false # Indicate something went wrong puts 'Finalized!' true end end Example.fulfill_promise(:generate_large_image, 'image data') ``` ---------------------------------------- Feature #21160: Local return from proc https://bugs.ruby-lang.org/issues/21160#change-112121 * Author: JustJosh (Joshua Stowers) * Status: Open ---------------------------------------- When writing DSL-style helper methods, I often store block arguments as procs to use as callbacks. Using `return` in a proc will return from the context it was created in, which is unsuitable in the following example. Since procs cannot be converted to lambdas, I end up using `next` to return a value from them early. Example: ``` ruby fulfills_promise :generate_large_image do |image_data| next false if image_data.nil? puts 'Saving image..' # etc. end ``` This works but confuses most readers. I propose introducing an alias for it that is more appropriate for this use case. Perhaps `pass` or `continue`? It's worth noting that `return` would work with `fulfills_promise :foo, -> (bar) do`, though it detracts a bit from a DSL's expressiveness. -- https://bugs.ruby-lang.org/

Issue #21160 has been updated by nobu (Nobuyoshi Nakada). Why not rescue `LocalJumpError`? ```ruby def self.fulfill_promise(promise_name, data) puts "Fulfilling promise: #{promise_name}" callback = @@promise_callbacks[promise_name] begin complete = callback.call(data) rescue LocalJumpError => e complete = e.exit_value end if complete puts 'Complete!' else puts 'Failed!' end end ``` This works with both of `return` and `break` in already released versions of Ruby, and even your DSL does not need to change. If we were add a new keyword, you would have to wait for the end of this year at least. ---------------------------------------- Feature #21160: Local return from proc https://bugs.ruby-lang.org/issues/21160#change-112123 * Author: JustJosh (Joshua Stowers) * Status: Open ---------------------------------------- When writing DSL-style helper methods, I often store block arguments as procs to use as callbacks. Using `return` in a proc will return from the context it was created in, which is unsuitable in the following example. Since procs cannot be converted to lambdas, I end up using `next` to return a value from them early. Example: ``` ruby fulfills_promise :generate_large_image do |image_data| next false if image_data.nil? puts 'Saving image..' # etc. end ``` This works but confuses most readers. I propose introducing an alias for it that is more appropriate for this use case. Perhaps `pass` or `continue`? It's worth noting that `return` would work with `fulfills_promise :foo, -> (bar) do`, though it detracts a bit from a DSL's expressiveness. -- https://bugs.ruby-lang.org/

Issue #21160 has been updated by JustJosh (Joshua Stowers). nobu (Nobuyoshi Nakada) wrote in #note-3:
Why not rescue `LocalJumpError`?
That is definitely a better solution than requiring developers to use `next`. Thank you for the suggestion. As I understand it, the reason `break` doesn't work in my example is because the proc is "orphaned." Ref: https://docs.ruby-lang.org/en/3.3/Proc.html#class-Proc-label-Orphaned+Proc If an orphan-friendly implementation or alternative to `break` is worth considering, I would be happy to take a shot. ---------------------------------------- Feature #21160: Local return from proc https://bugs.ruby-lang.org/issues/21160#change-112138 * Author: JustJosh (Joshua Stowers) * Status: Open ---------------------------------------- When writing DSL-style helper methods, I often store block arguments as procs to use as callbacks. Using `return` in a proc will return from the context it was created in, which is unsuitable in the following example. Since procs cannot be converted to lambdas, I end up using `next` to return a value from them early. Example: ``` ruby fulfills_promise :generate_large_image do |image_data| next false if image_data.nil? puts 'Saving image..' # etc. end ``` This works but confuses most readers. I propose introducing an alias for it that is more appropriate for this use case. Perhaps `pass` or `continue`? It's worth noting that `return` would work with `fulfills_promise :foo, -> (bar) do`, though it detracts a bit from a DSL's expressiveness. -- https://bugs.ruby-lang.org/
participants (3)
-
JustJosh (Joshua Stowers)
-
nobu (Nobuyoshi Nakada)
-
ufuk (Ufuk Kayserilioglu)