当前关于变量绑定目标 Rebol 2的深入文档。有人可以提供 Rebol 2 和 3 之间差异的摘要吗?
1 回答
某处没有真正的总结,所以让我们回顾一下基础知识,也许比Bindology更非正式一些。让拉迪斯拉夫为 R3 和 Red 写一篇新的论文。我们将按重要性顺序讨论基本差异。
对象和函数上下文
这是最大的区别。
在 R2 中,基本上有两种上下文:常规对象上下文和system/words
. 两者都有静态绑定,这意味着一旦bind
函数运行,单词绑定指向一个带有真实指针的特定对象。
system/words
上下文可以在运行时扩展以包含新词,但所有其他对象都没有。函数使用常规对象上下文,当您递归调用函数时,使用一些技巧来切换值块。
该词self
只是对象上下文中第一个词出现的常规词,具有显示技巧以不显示上下文中的第一个词;函数上下文没有那个词,所以它们没有正确显示第一个常规词。
在 R3 中,几乎所有这些都不同。
在 R3 中还有两种上下文:常规上下文和本地堆栈上下文。对象、模块、绑定循环、基本上除了函数之外的所有内容都使用常规上下文use
,并且它们可以像system/words
was 一样可扩展(是的,“was”,我们会谈到这一点)。旧的固定长度对象不见了。函数使用堆栈本地上下文,它(除非我们还没有看到任何错误)不应该是可扩展的,因为这会弄乱堆栈帧。与旧的一样,system/words
您不能缩小上下文,因为从上下文中删除单词会破坏这些单词的任何绑定。
如果您想将单词添加到常规上下文中,您可以使用bind/new
、bind/set
、resolve/extend
或append
,或其他调用它们的函数,具体取决于您需要的行为。这是R3中bind
and函数的新行为。append
与以前一样,单词与常规和堆栈本地上下文的绑定是静态的。值查找是另一回事。对于常规上下文,值查找非常直接,通过一个简单的指针间接指向一个静态值槽块来完成。对于堆栈本地上下文,值块由堆栈帧链接到并从那里引用,因此要找到正确的帧,您必须执行 O(stack-depth) 的堆栈遍历。有关详细信息,请参阅错误 #1946 - 我们稍后会讨论原因。
哦,self
它不再是一个常规词,它是一个绑定技巧,一个关键字。当您将词块绑定到对象或模块上下文时,它会绑定self
评估为对上下文的引用的关键字。但是,可以设置一个内部标志,表示上下文是“无私的”,从而关闭该self
关键字。关闭该关键字后,您实际上可以将该词self
用作上下文中的字段。绑定循环use
和函数上下文为它们的上下文设置了无私标志,并且selfless?
函数会对此进行检查。
这个模型在相当复杂的 CureCode 火焰战争中得到了改进和记录,就像 R2 的模型在 1999-2000 年被 REBOL 邮件列表火焰战争记录下来一样。:-)
函数与闭包
当我在上面谈论堆栈本地函数上下文时,我指的是function!
类型函数使用的上下文。R3 有很多函数类型,但它们中的大多数都是本机函数,并且本机函数不使用这些堆栈本地上下文(尽管它们确实获取堆栈帧)。唯一适用于 Rebol 代码的函数类型是function!
和一个新closure!
类型。闭包与常规函数非常不同。
当您创建一个function!
时,您正在创建一个函数。它构造一个堆栈本地上下文,将代码体绑定到它,并将代码体和规范捆绑在一起。当您调用该函数时,它会创建一个包含对该函数上下文的引用的堆栈帧并运行代码块。如果它在函数上下文中有访问字,它会进行堆栈遍历以找到正确的帧,然后从那里获取值。非常坦率的。
closure!
另一方面,当您创建 builder 时,您会创建一个function builder。它设置规范和函数function!
体几乎与在副本中。然后,当它复制主体时,所有闭包词引用与对象上下文的引用一样静态。bind/copy
两者之间的另一个区别在于它们在函数运行之前、函数运行时以及函数运行完成之后的行为方式。
在 R2中,function!
当函数未运行时,上下文仍然存在,但函数顶层调用的值块仍然存在。只有递归调用才能获得新的值块,顶级调用保持一个持久的值块就像我说的,hackery。更糟糕的是,函数返回时不会清除顶级值块,因此您最好确保没有引用任何敏感内容或在函数返回时要回收(使用also
函数清理,这就是我为)。
在 R3 中,function!
当函数未运行时,上下文仍然存在,但值块根本不存在。所有函数调用的行为都类似于 R2 中的递归调用,但更好的是因为它一直是这样设计的,而是引用堆栈帧。该堆栈框架的范围是动态的(如果您需要它的历史,请跟踪一个 Lisp 粉丝),所以只要该函数在当前堆栈上运行(是的,“当前”,我们会解决这个问题),你可以使用其中一个词来获取该函数最近一次调用的值。一旦函数的所有嵌套调用都返回,范围内将没有任何值,您只会触发一个错误(错误的错误,但我们会修复它)。
绑定到我的待办事项列表中的范围外功能词也有一个无用的限制,很快就会修复。有关详细信息,请参阅错误 #1893。
对于closure!
函数,在闭包运行之前,上下文根本不存在。一旦闭包开始运行,上下文就会被创建并持久存在。如果您再次或递归地调用闭包,则会创建另一个持久上下文。任何从闭包中泄露出来的词只指在闭包的特定运行期间创建的上下文。
当函数或闭包未运行时,您无法在 R3 中将单词绑定到函数或闭包上下文。对于函数,这是一个安全问题。对于闭包,这是一个定义问题。
闭包被认为非常有用,以至于 Ladislav 和我都将它们移植到 R2,在不同的时间独立地产生相似的代码,这很奇怪。我认为 Ladislav 的版本早于 R3,并作为 R3closure!
类型的灵感来源;我的版本基于测试该类型的外部行为并尝试在 R2 中为 R2/Forward 复制它,所以有趣的是,closure
最终的解决方案与 Ladislav 的原始解决方案非常相似,直到很久以后我才看到。我的版本从 2.7.7 开始包含在 R2 本身中,作为closure
,to-closure
和closure?
函数,并且这个closure!
词被分配了与 R2 中相同的类型值function!
。
全球与本地环境
这就是事情变得非常有趣的地方。
在Bindology中,有相当多的文章讨论了“全局”上下文(结果是system/words
)和“本地”上下文之间的区别,这对于 R2 来说是一个相当重要的区别。在 R3 中,这种区别是无关紧要的。
在 R3 中,system/words
消失了。没有一个“全局”上下文。就 R2 中的含义而言,所有常规上下文都是“本地”的,这使得“本地”的含义变得毫无用处。对于 R3,我们需要一组新的术语。
对于 R3,唯一重要的区别是上下文是否与任务相关,因此“全局”上下文的唯一有用含义是那些不直接与任务相关的上下文,而“本地”上下文是那些与任务相关的上下文. 在这种情况下,“任务”将是task!
类型,它基本上是当前模型中的操作系统线程。
目前,在 R3 中,目前唯一(几乎)与任务相关的是堆栈变量,这意味着堆栈相关的函数上下文也应该是任务相关的。这就是为什么堆栈遍历是必要的,因为否则我们需要在每个函数上下文中保留和维护 TLS 指针。所有常规上下文都是全局的。
另一件要考虑的事情是,根据计划(目前为止大部分都没有实现),用户上下文system/contexts/user
和system
它本身也应该是任务相关的,所以即使按照 R3 标准,它们也会被认为是“本地的”。由于system/contexts/user
基本上是 R3 与 R2's 最接近的东西system/words
,这意味着脚本认为是它们的“全局”上下文实际上应该是R3 中的任务本地。
R3 确实有几个称为sys
and的系统全局上下文lib
,尽管它们的使用方式与 R2 的全局上下文完全不同。此外,所有模块上下文都是全局的。
有可能(并且很常见)全局定义的上下文仅从任务本地根引用中引用,因此这将使这些上下文间接地起作用 task-local。这是从“用户代码”调用绑定循环、闭包或私有模块时通常发生的情况use
,这基本上意味着非模块脚本,它们被绑定到system/contexts/user
. 从技术上讲,从模块调用的函数也是如此(因为函数是堆栈本地的),但这些引用通常最终分配给模块字,它们是全局的。
不,我们还没有同步。尽管如此,这仍然是 R3 的设计最终应该拥有的模型,并且部分已经做到了。有关更多详细信息,请参阅模块绑定文章。
作为奖励,R3 现在有一个真实的符号表,而不是system/words
用作临时符号表。这意味着 R2 用于快速达到的字数限制在 R3 中有效地消失了。我不知道有任何应用程序达到了新的限制,甚至没有确定限制有多高,尽管它显然远远超过了数百万个不同的符号。现在我们可以访问它,我们应该检查源以弄清楚这一点。
加载和使用
末节。该use
函数用 初始化它的单词none
而不是让它们未设置。而且因为没有像 R2 中那样的“全局”上下文,load
所以根本不一定绑定单词。绑定到哪个上下文load
取决于模块绑定文章中提到的情况,但除非您另外指定,否则它会显式地将单词绑定到system/contexts/user
. 现在两者都是夹层功能。
拼写和别名
R3 在这方面与 R2 基本相同,默认情况下单词绑定不区分大小写。单词本身是保留大小写的,如果您使用区分大小写的方法比较它们,您会发现单词之间的差异仅在大小写上有所不同。
但是,在对象或函数上下文中,当一个词被映射到一个值槽时,另一个词被绑定到该上下文或在运行时查找,仅大小写不同的词被认为是有效的同一个词并映射到同一个词价值槽。
然而,我们发现显式创建的别名是用alias
函数创建的,其中别名单词的拼写在其他方面有所不同,而不仅仅是大小写破坏了对象和函数上下文。在 R2 中,他们在 中解决了这些问题system/words
,这使得alias
除了演示之外的其他任何东西都很难使用,而不是非常危险。
因此,我们alias
完全删除了外部可见功能。内部别名工具仍然有效,因为它只对通常被认为等效于上下文查找的单词进行别名,这意味着上下文不会中断。但是我们现在建议alias
使用在演示中使用的本地化和其他技巧(如果从未在实践中使用过)使用老式方法为具有新拼写的另一个单词分配值。
单词类型
该issue!
类型现在是单词类型。你可以绑定它。到目前为止,没有人利用能够绑定问题的优势,而只是利用提高的操作速度。
就是这样,对于有意的更改。其余大部分差异可能是上述的副作用,甚至可能是错误或尚未实现的功能。R3 中甚至可能存在一些类似 R2 的行为,这也是错误或尚未实现的功能的结果。如有疑问,请先询问。