【问题标题】:What is the summary of the differences in binding behaviour between Rebol 2 and 3?Rebol 2 和 3 之间的绑定行为差异的总结是什么?
【发布时间】:2013-01-26 22:04:20
【问题描述】:

当前深入的documentation on variable binding 目标是 Rebol 2。有人可以提供 Rebol 2 和 3 之间差异的摘要吗?

【问题讨论】:

    标签: binding bind rebol rebol3 rebol2


    【解决方案1】:

    在某处并没有真正的总结,所以让我们回顾一下基础知识,也许比Bindology 更非正式一点。让拉迪斯拉夫为 R3 和 Red 写一篇新的论文。我们将按重要性顺序讨论基本差异。

    对象和函数上下文

    这就是最大的不同。

    在 R2 中,基本上有两种上下文:常规对象上下文和system/words。两者都有静态绑定,这意味着一旦运行 bind 函数,单词 binding 就会指向一个带有真实指针的特定对象。

    system/words 上下文能够在运行时扩展以包含新词,但所有其他对象都不能。函数使用常规的对象上下文,当你递归调用函数时,有一些技巧可以切换出值块。

    self 词只是在对象上下文中出现在第一个词上的常规词,显示技巧是不显示上下文中的第一个词;函数上下文没有那个词,所以它们没有正确显示第一个常规词。

    在 R3 中,几乎所有这些都不同。

    在 R3 中还有两种上下文:常规上下文和堆栈本地上下文。常规上下文被对象、模块、绑定循环、use 使用,基本上除了函数之外的所有东西,它们可以像system/words was 一样扩展(是的,“was”,我们会谈到那个)。旧的固定长度对象不见了。函数使用堆栈本地上下文,它(除非我们还没有看到任何错误)不应该是可扩展的,因为这会弄乱堆栈帧。与旧的 system/words 一样,您不能缩小上下文,因为从上下文中删除单词会破坏这些单词的任何绑定。

    如果您想将单词添加到常规上下文中,您可以使用bind/newbind/setresolve/extendappend,或调用这些的其他函数,具体取决于您需要的行为。这是 R3 中 bindappend 函数的新行为。

    单词与常规和堆栈本地上下文的绑定是静态的,就像以前一样。值查找是另一回事。对于常规上下文,值查找非常直接,通过一个简单的指针间接指向一个静态值槽块来完成。对于堆栈本地上下文,值块由堆栈帧链接到并从那里引用,因此要找到正确的帧,您必须执行 O(stack-depth) 的堆栈遍历。有关详细信息,请参阅 bug #1946 - 我们稍后会解释原因。

    哦,self 不再是一个常规词,它是一个绑定技巧,一个关键字。当您将词块绑定到对象或模块上下文时,它会绑定关键字self,它的计算结果是对上下文的引用。但是,可以设置一个内部标志,表示上下文是“无私的”,这会关闭 self 关键字。关闭该关键字后,您实际上可以在上下文中使用单词self 作为字段。绑定循环、use 和函数上下文为其上下文设置了无私标志,selfless? 函数会对此进行检查。

    此模型在相当复杂的 CureCode 火焰战中得到改进和记录,就像 R2 的模型在 1999-2000 年的 REBOL 邮件列表火焰战中记录的一样。 :-)

    函数与闭包

    当我在上面谈论堆栈本地函数上下文时,我指的是function! 类型函数使用的上下文。 R3 有很多函数类型,但它们中的大多数都是本机函数,并且本机函数不使用这些堆栈本地上下文(尽管它们确实获取堆栈帧)。唯一适用于 Rebol 代码的函数类型是 function! 和一个新的 closure! 类型。闭包与常规函数非常不同。

    当您创建function! 时,您正在创建一个函数。它构造一个堆栈本地上下文,将代码体绑定到它,并将代码体和规范捆绑在一起。当您调用该函数时,它会创建一个包含对该函数上下文的引用的堆栈帧并运行代码块。如果它在函数上下文中有访问字,它会进行堆栈遍历以找到正确的帧,然后从那里获取值。相当直截了当。

    另一方面,当您创建closure! 时,您创建了一个函数构建器。它设置规范和函数体与function! 几乎相同,但是当您调用 闭包时,它会创建一个新的常规 无私上下文,然后执行@987654353 @ ,将所有对函数上下文的引用更改为对副本中新常规上下文的引用。然后,当它复制正文时,所有闭包词引用都与对象上下文的引用一样静态。

    两者之间的另一个区别在于它们在函数运行之前、函数运行时以及函数运行完成之后的行为方式。

    在 R2 中,function! 上下文在函数未运行时仍然存在,但函数顶层调用的值块也仍然存在。只有递归调用才能获得新的值块,顶级调用保持一个持久的值块就像我说的,hackery。更糟糕的是,当函数返回时,顶层值块不会被清除,所以你最好确保你没有引用任何敏感的东西或者你想要在函数返回时回收(使用also函数来清理,这就是我做它的目的)。

    在 R3 中,当函数未运行时,function! 上下文仍然存在,但值块根本不存在。所有函数调用的行为都类似于 R2 中的递归调用,但更好的是因为它一直是这样设计的,而是引用堆栈帧。该堆栈框架的范围是动态的(如果您需要它的历史,请跟踪一个 Lisp 粉丝),所以只要该函数在当前堆栈上运行(是的,“当前”,我们会解决这个问题),你可以使用其中一个词来获取该函数最近一次调用的值。一旦函数的所有嵌套调用都返回,范围内将没有任何值,您只会触发一个错误(错误的错误,但我们会修复它)。

    对于绑定到我的待办事项列表中的超出范围的功能词还有一个无用的限制,很快就会修复。详情请见bug #1893

    对于closure! 函数,在闭包运行之前上下文根本不存在。一旦闭包开始运行,上下文就会被创建并持久存在。如果你再次调用闭包,或者递归调用,另一个持久上下文被创建。任何从闭包中泄露出来的词都只指在闭包的特定运行期间创建的上下文。

    当函数或闭包未运行时,您无法在 R3 中将单词绑定到函数或闭包上下文。对于函数,这是一个安全问题。对于闭包,这是一个定义问题。

    闭包被认为非常有用,以至于我和 Ladislav 都将它们移植到了 R2,在不同的时间独立地产生了相似的代码,这很奇怪。我认为 Ladislav 的版本早于 R3,并且是 R3 的 closure! 类型的灵感来源;我的版本是基于测试该类型的外部行为并尝试在 R2 中为 R2/Forward 复制它,所以有趣的是 closure 的解决方案最终与 Ladislav 的原始解决方案非常相似,直到我才看到很久以后。我的版本从 2.7.7 开始包含在 R2 本身中,作为 closureto-closureclosure? 函数,并且 closure! 字被分配了与 R2 中的 function! 相同的类型值。

    全局与本地上下文

    这才是真正有趣的地方。

    Bindology 中有相当多的文章讨论了“全局”上下文(原来是system/words)和“本地”上下文之间的区别,这对于 R2 来说是一个相当重要的区别。在 R3 中,这种区别是无关紧要的

    在 R3 中,system/words消失。没有一个“全局”上下文。就 R2 中的含义而言,所有常规上下文都是“本地”的,这使得“本地”的含义变得毫无用处。对于 R3,我们需要一组新的术语。

    对于 R3,唯一重要的区别是上下文是否任务相关,因此“全局”上下文的唯一有用含义是那些不直接与任务相关的上下文,以及“本地”上下文" 上下文是与任务相关的。在这种情况下,“任务”将是 task! 类型,它基本上是当前模型中的 OS 线程。

    目前,在 R3 中,目前唯一(几乎)与任务相关的是堆栈变量,这意味着堆栈相关的函数上下文应该是 task-relative也。这就是为什么stack walk 是必要的,因为否则我们需要在每个函数上下文 中保留和维护 TLS 指针。所有常规上下文都是全局的。

    另一件需要考虑的事情是,根据计划(目前大部分未实现),用户上下文 system/contexts/usersystem 本身也旨在与任务相关,因此即使按照 R3 标准,它们也将是被认为是“本地的”。由于system/contexts/user 基本上是 R3 与 R2 的 system/words 最接近的东西,这意味着 scripts 认为的“全局”上下文实际上应该是 task- R3 中的本地

    R3 确实有两个称为syslib 的系统全局上下文,尽管它们的使用方式与R2 的全局上下文完全不同。此外,所有模块上下文都是全局的。

    可能(并且很常见)全局定义的上下文仅从任务本地根引用中引用,因此这会使这些上下文间接地在任务本地生效。这是从“用户代码”调用绑定循环、use、闭包或私有模块时通常发生的情况,这基本上意味着绑定到system/contexts/user 的非模块脚本。从技术上讲,从模块调用的函数也是如此(因为函数是堆栈本地的),但这些引用通常最终分配给模块字,它们是全局的。

    不,我们还没有同步。尽管如此,这仍然是 R3 的设计最终应该拥有的模型,并且部分已经做到了。有关详细信息,请参阅module binding article

    作为奖励,R3 现在有一个真实的符号表,而不是使用 system/words 作为临时符号表。这意味着 R2 用于快速达到的字数限制在 R3 中有效地消失了。我不知道有任何应用程序达到了新的限制,甚至没有确定限制有多高,尽管它显然远远超过了数百万个不同的符号。既然我们可以访问它,我们应该检查源以弄清楚这一点。

    加载和使用

    小细节。 use 函数用none 初始化它的单词,而不是让它们未设置。而且由于没有像 R2 中那样的“全局”上下文,load 根本不一定绑定单词。 load 绑定到哪个上下文取决于module binding article 中提到的情况,但除非您另外指定,否则它会将单词显式绑定到system/contexts/user。现在两者都是夹层函数。

    拼写和别名

    R3 在这方面与 R2 基本相同,默认情况下单词绑定不区分大小写。单词本身是保留大小写的,如果您使用区分大小写的方法比较它们,您会发现单词之间的差异仅在大小写上有所不同。

    但是,在对象或函数上下文中,当一个词被映射到一个值槽时,另一个词被绑定到该上下文或在运行时查找,仅大小写不同的词被认为是有效的同一个词和映射到相同的值槽。

    但是,我们发现使用 alias 函数创建的显式别名,其中别名单词的拼写在其他方面存在差异,而不仅仅是大小写严重破坏了对象和函数上下文。在 R2 中,他们在 system/words 中解决了这些问题,这使得 alias 在演示之外的任何内容中使用起来都太尴尬了,而不是非常危险。

    因此,我们完全删除了外部可见的alias 函数。内部别名工具仍然有效,因为它只对通常被认为等效于上下文查找的单词进行别名,这意味着上下文不会中断。但我们现在建议,如果在实践中从未使用过 alias 在演示中使用的本地化和其他技巧,请使用旧式方法为具有新拼写的另一个单词赋值。

    单词类型

    issue! 类型现在是单词类型。你可以绑定它。到目前为止,还没有人利用能够绑定问题的能力,而只是利用提高的操作速度。

    就是这样,有意的改变。其余大部分差异可能是上述的副作用,甚至可能是错误或尚未实现的功能。 R3 中甚至可能存在一些类似 R2 的行为,这也是错误或尚未实现的功能的结果。如有疑问,请先询问。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-07
      • 1970-01-01
      • 2017-06-03
      • 1970-01-01
      • 2014-03-11
      • 2015-08-18
      相关资源
      最近更新 更多