Hacker News new | ask | show | jobs
by chowells 394 days ago
Given that you completely ignored what I said I wanted to do and gave an answer for some other question, I'm pretty sure it's more complicated than you think.

I want to write a method that takes a block and distinguishes between next and break, exactly like the methods of enumeration do. It's obviously possible because a super common interface does it.

Last time I looked, that interface does it by being written in native code that interfaces with the interpreter. That is, it's not part of the language semantics. It's a special case with weird rules unlike what anything else gets to do.

Or at least it was. Maybe the language has actually made it accessible since then, but I'm not optimistic. That's not the ruby way.

2 comments

Your sentence structure did not at all make it clear that "the two" referred to next and break rather than proc and lambda.

Assuming you don't care whether a "next" was actually called, but only whether you've exited the block and whether or not you exited it via a break, you can do this check in a number of ways, but it I will agree it's a bit dirty that sometimes you do need to rely on standard library functionality rather than language constructs if you want to do these things.

Here's one way of doing it, since "break" within a Fiber triggers a LocalJumpError:

    def next_or_break(&block)
      Fiber.new(&block).resume
      :next_or_end_of_block_reached
    rescue LocalJumpError
      :break
    end

    p(next_or_break do next end)
    p(next_or_break do break end)
> but it I will agree it's a bit dirty that sometimes you do need to rely on standard library functionality rather than language constructs if you want to do these things

You don't need to introduce a Fiber or rely on the standard library, you can just use the core language, leveraging the fact that code in ensure sections gets called even when you are returning "past" the calling method.

(You may need a dirty catch-all rescue so you can set a flag before reraising that lets you distinguish "bypassing direct return by exception" from "bypassing direct return by break or proc-semantics return", but that's still the core language, not standard library.)

I put example code in another comment: https://news.ycombinator.com/item?id=44065226

Ah, of course. I'm embarassed I didn't think of that now... That's absolutely better than abusing Fiber. Great suggestion.
I think you’re seeing the base language.

Effectively blocks are self-contained chunks of code. You can change things around them, but you can’t change how keywords work inside them. Because you’re crossing a method boundary when you call a block you’re not able to access next and break. (Or capture return.)

Ruby is defining scope here and C methods are not limited by the language they define.

Exactly. You can leverage some of the standard library methods to figure out if a block did a next or exit normally vs. did a break, by e.g. abusing Fiber's (there are probably other ways too), and that might feel a bit dirty, but those are also part of the language.

I think it'd be nice if Ruby language constructs offered a few of the building blocks used by the standard library so that you could implement more of the standard library without dipping down into some low level mechanism - for my prototype Ruby compiler a priority was to implement as much as possible in Ruby, and there are certainly more things in the core classes that can't be done in pure Ruby than I'd like.

But it's not that much, and while there are plenty of warts, the inconsistencies are often smaller than people think.