Ruby中在带有隐式块的方法中不再支持Proc.new吗?

huangapple go评论65阅读模式
英文:

Ruby Proc.new in method with implicit block no longer supported?

问题

这是Matz的书中的一个小节:

如果在具有关联块的方法内部调用Proc.new而没有块,那么它会返回一个代表包含方法关联块的proc。以这种方式使用Proc.new提供了一种替代方法,用于在方法定义中使用带有“&”前缀的块参数。例如,以下两种方法是等效的:

def invoke(&b) 
  b.call
end

def invoke 
  Proc.new.call
end

这是我的理解。然而,在升级到Ruby v. 3.1.2后,似乎不再是这种情况,这两种方法不再等效。现在,如果我调用这两种方法:

def invoke(&b) 
  b.call
end

def invoke2 
  Proc.new.call
end

invoke {puts 'hello'}
invoke2 {puts 'hello'}

输出结果:

hello
test.rb:6:in `new': tried to create Proc object without a block (ArgumentError)

我寻找了原因,并在这里找到了一些有趣的信息:https://stackoverflow.com/questions/61990955/can-a-ruby-method-access-the-implicit-block-argument 在这个帖子中,Stefan的回答引用了2.7.1版本的文档中的以下内容:

::new may be called without a block only within a method with an attached block, in which case that block is converted to the Proc object.

然而,这段文字在3.1.2版本的文档中神秘地消失了。

Jörg W Mittag在他的回答中提出了一个有趣的观点:

[这]实际上是MRI中Proc::new如何实现的一个意外副作用:Proc::new不会检查您是否传递了块,它只是假定您传递了块,并且会从内部VM堆栈的顶部获取第一个块。因此,如果您没有将块传递给Proc::new,实际上会创建一个代表传递给方法的隐式块的Proc(因为它刚好位于堆栈的顶部)。

但是,这从来不是可移植的,不受保证,不在所有Ruby实现中工作,据我所知,在YARV中也不再起作用。

这些信息表明,这个功能可能随时消失。

通过查阅文档存档,我发现最后一个提到能够这样做的版本是3.0.5。接下来的版本3.1.0删除了引用的段落,没有任何庆祝活动。

我正在寻找的是某种明确的声明,说明从某个版本开始不再支持在没有显式块参数的方法中使用Proc.new这种用法。有人有这样的声明吗?

我更广泛的关注是,我曾经认为Matz的书是真理的源泉,但现在我发现它在一个小部分已经过时。有没有其他Matz的书中过时的例子,是否有某种文档列出了从版本到版本的所有功能更改?

英文:

This is a small excerpt from Matz's book:

>If Proc.new is invoked without a block from within a method that does have an asso- ciated block, then it returns a proc representing the block associated with the containing method. Using Proc.new in this way provides an alternative to using an ampersand-prefixed block argument in a method definition. The following two methods are equivalent, for example:

def invoke(&b) 
  b.call
end

def invoke 
    Proc.new.call
end

This has been my understanding. However, after upgrading to Ruby v. 3.1.2, it seems that it is no longer the case that the methods are equivalent. Now, if I invoke both of these methods, so:

def invoke(&b) 
  b.call
end

def invoke2 
  Proc.new.call
end

invoke {puts 'hello'}
invoke2 {puts 'hello'}

hello
test.rb:6:in `new': tried to create Proc object without a block (ArgumentError)

I've looked around for why, and found some interesting information here:
https://stackoverflow.com/questions/61990955/can-a-ruby-method-access-the-implicit-block-argument In this, Stefan's answer quotes the doc from v. 2.7.1 thus:
>::new may be called without a block only within a method with an attached block, in which case that block is converted to the Proc object.

However, this bit of prose has mysteriously disappeared from the 3.1.2 doc.

Jörg W Mittag has an interesting take in his answer:

>[This] was actually an unintended side-effect of how Proc::new was implemented in MRI: Proc::new did not check whether you passed a block or not, it simply assumed that you passed a block and would take the first block off the top of the internal VM stack. So, if you didn't pass a block to Proc::new, it would actually end up creating a Proc for the implicit block that was passed to the method (since that was the one which just happened to be on the top of the stack).

>But, that was never portable, never guaranteed, never worked in all Ruby implementations, and AFAIK no longer works in YARV.

This information suggests that the feature could have disappeared at any time.

Digging through the documentation archives, I find that the last version that references being able to do this is 3.0.5. The next version, 3.1.0, drops the quoted paragraph with no fanfare.

What I'm looking for is some sort of definitive statement somewhere that this use of Proc.new in a method without an explicit block argument is no longer supported as of version such and so. Does anyone have such?

My broader concern is that I have thought of Matz's book as a source of truth, and now I find that in one small part it is obsolete. Does anyone have any further instances of obsolescence in Matz's book, and is there some sort of documentation somewhere that lists all the feature changes from version to version?

答案1

得分: 1

以下是翻译好的部分:

TL;DR

感谢@Stefan提供错误跟踪链接,以及其他评论者提供的各种解释,您所指的功能在8年多前就已经提出了弃用和移除的票

带有链接和引用的详细解释

因此,Ruby当前不符合您的期望以及原始Ruby书籍不匹配所有Ruby实现的一些原因包括:

  1. 使用不同的编译器、语言、运行时和虚拟机的替代实现是一个不断变化的目标,当前Ruby生态系统的许多内容都没有包含在原始书籍中。自那时以来发生了许多变化。关于这一点,后面会有更多说明。

  2. 这个(误导性的)功能在Ruby 3.0.0中已经移除,但直到将此提交合并到Ruby v3_0_0_rc2-166-g8da7f4abc7的_proc.c_中后,文档才得以更新。

  3. 您所提到的书籍至少是在15年前编写和出版的。这本书的引用仍然可以在亚马逊和O'Reilly上找到:

    _Ruby编程语言:您需要了解的一切,第1版。_Flanagan, David和松本行弘。O'Reilly Media;2008年1月。

  4. 尽管从语言设计的角度来看,您引用的书中仍然有很多有价值的材料,但自那时以来,Ruby发生了很多变化,因此不再应该被视为Ruby解释器或虚拟机实现各种内部的权威来源。

  5. 还有一些关注Ruby内部的其他书籍,但据我所知,没有一本真正适用于Ruby >= 3.2.2。

  6. 《Pragmatic Programmers》即将出版的书籍,名为《Programming Ruby 3.2, 第5版》,由Noel Rappin等人编写,可能会更加最新,但beta版书籍的大纲表明,它不太关注内部细节,而更有可能涵盖重要的变化。

    注:无论这种特定用例是否足够重要,以至于当前附录标题为“Ruby更改”的附录是否足够重要,我无法猜测,但由于这本书还处于beta版阶段,如果您认为它很重要,您可以随时参与beta版并建议其包含。

  7. 根据定义,了解Ruby语言实现方式的权威来源是C和YARV的参考实现的GitHub上的源代码。其他Ruby实现,如TruffleRubyJRuby可能以不同的方式或根本不实现这些功能。

  8. 参考实现维护一个问题跟踪器,可以用于发现关于过去、待处理或被拒绝的功能更改的讨论,但在涉及代码实现方式时,它不一定具有与实际源代码相同的权威性,尽管它应该明确指出影响它们的提交。

  9. 从2.0.0开始,参考实现的官方文档都可以在<https://docs.ruby-lang.org/en/>上找到,但许多人还依赖<https://ruby-doc.org/>。目前,后者包含有关点发布的信息,而前者则不包括。个人体验可能有所不同。

  10. 继续将旧书作为了解Ruby设计的参考,但请迁移到更新的书籍和权威来源,以获取有关特定实现问题的答案。如果您讨论不常见的用法、边缘情况、替代实现或特定平台问题,这一点尤为重要。

英文:

TL;DR

With credit to @Stefan for the bug tracker link, and to other commenters for various explanations, the feature you're referring to had a ticket opened for deprecation and removal over 8 years ago.

So, some of the reasons Ruby doesn't currently do what you expect and why the original Ruby book doesn't match all Ruby implementations include:

  1. Alternative implementations, using different compilers, languages, runtimes, and virtual machines is a moving target and a lot of the current Ruby ecosystem aren't covered in the original book. A lot has changed since then. More on this below.

  2. The (mis-)feature was removed in Ruby 3.0.0, but the documentation wasn't updated to match until this commit was merged into proc.c in Ruby v3_0_0_rc2-166-g8da7f4abc7.

  3. The book you're referring to was written and published at least 15 years ago. The citation for the book, which is still available on Amazon and from O'Reilly is:

    > The Ruby Programming Language: Everything You Need to Know, 1st Edition. Flanagan, David and Yukihiro Matsumoto. O'Reilly Media; January 2008.

  4. While there's still a lot of good material in the book you're referencing from a language design perspective, a lot has changed in Ruby since then so it should no longer be considered a canonical source for how the Ruby interpreter or virtual machine implements various internals.

  5. There are some other books that focus on Ruby internals, but as far as I know none of them are really up to date for Ruby >= 3.2.2.

  6. It is possible that the forthcoming book from Pragmatic Programmers entitled Programming Ruby 3.2, 5th Ed. by Noel Rappin et al. will be more up to date, but the outline of the book in beta suggests that it isn't spilling a lot of ink over internals but is likely to cover notable changes.

    NB: Whether or not this particular use case will be notable enough for the appendix currently titled "Ruby Changes" is not something I can guess at, but since the book is in beta you could always participate in the beta and suggest its inclusion if you feel it's important.

  7. By definition, the canonical source for how the Ruby language is implemented is the source code on GitHub for the reference implementation with C and YARV. Other Ruby immplementations such as TruffleRuby or JRuby may implement things differently or not at all.

  8. The reference implementation maintains an issue tracker which can be useful for discovering discussions about past, pending, or rejected feature changes, but is not necessarily canonical in the same way the actual source code is when it comes to how code is implemented, although it should certainly point to commits that affect them.

  9. The official documentation for the reference implementations as far back as 2.0.0 are available on <https://docs.ruby-lang.org/en/>, but many people also rely on <https://ruby-doc.org/>. Currently, the latter includes information about point releases while the former does not. YMMV.

  10. Continue to use the older book as a reference about how Ruby was designed, but please migrate to newer books and canonical sources for questions about specific implementation issues. This is especially important if you're talking about uncommon usages, edge cases, alternative implementations, or platform-specific issues.

huangapple
  • 本文由 发表于 2023年7月11日 12:48:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76658788.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定