Rebol模块中的单词是如何绑定的? [英] How are words bound within a Rebol module?

查看:70
本文介绍了Rebol模块中的单词是如何绑定的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道module!类型为受保护的名称空间提供了比object!'use函数更好的结构.单词如何在模块内绑定—我注意到一些与未绑定单词有关的错误:

I understand that the module! type provides a better structure for protected namespaces than object! or the 'use function. How are words bound within the module—I notice some errors related to unbound words:

REBOL [Type: 'module] set 'foo "Bar"

此外,Rebol如何区分模块本地的单词('foo)和系统功能的本地单词('set)?

Also, how does Rebol distinguish between a word local to the module ('foo) and that of a system function ('set)?

不久之后的次要更新:

我看到有一个开关可以更改绑定方法:

I see there's a switch that changes the method of binding:

REBOL [Type: 'module Options: [isolate]] set 'foo "Bar"

这有什么不同?默认情况下使用此方法有什么陷阱?

What does this do differently? What gotchas are there in using this method by default?

推荐答案

好的,这有点棘手.

在Rebol 3中,没有系统单词,只有单词.一些单词已添加到运行时库lib中,而set是其中的一个,恰好为其分配了一个功能.模块从lib导入单词,尽管导入"的含义取决于模块选项.这可能比您预期的要棘手,所以让我解释一下.

In Rebol 3 there are no such things as system words, there are just words. Some words have been added to the runtime library lib, and set is one of those words, which happens to have a function assigned to it. Modules import words from lib, though what "import" means depends on the module options. That might be more tricky than you were expecting, so let me explain.

对于初学者来说,我将介绍常规"模块的导入方式,常规"模块没有指定任何选项.让我们从第一个模块开始:

For starters, I'll go over what importing means for "regular" modules, ones that don't have any options specified. Let's start with your first module:

REBOL [Type: 'module] set 'foo "Bar"

首先,您在这里有一个错误的假设:单词foo在模块的本地不是,与set相同.如果要将foo定义为本地单词,则必须使用与对象相同的方法,请将该单词用作顶级设置词,例如:

First of all, you have a wrong assumption here: The word foo is not local to the module, it's just the same as set. If you want to define foo as a local word you have to use the same method as you do with objects, use the word as a set-word at the top level, like this:

REBOL [Type: 'module] foo: "Bar"

fooset之间的唯一区别是您尚未导出或在lib中添加单词foo.当您在尚未声明为本地单词的模块中引用单词时,它必须从某个位置获取它们的值和/或绑定.对于常规模块,它首先将代码绑定到lib,然后通过将代码再次绑定到模块的本地上下文来覆盖该代码.在本地上下文中定义的任何单词都将绑定到该单词.在本地上下文中未定义的任何单词都将保留其旧的绑定,在这种情况下为lib.这就是导入"对于常规模块的含义.

The only difference between foo and set is that you hadn't exported or added the word foo to lib yet. When you reference words in a module that you haven't declared as local words, it has to get their values and/or bindings from somewhere. For regular modules, it binds the code to lib first, then overrides that by binding the code again to the module's local context. Any words defined in the local context will be bound to it. Any words not defined in the local context will retain their old bindings, in this case to lib. That is what "importing" means for regular modules.

在第一个示例中,假设您自己还没有这样做,则不会提前将单词foo添加到运行时库中.这意味着foo未绑定到lib,并且由于未将其声明为本地单词,因此也未绑定到本地上下文.因此,结果foo根本没有任何约束.在您的代码中这是一个错误,但在其他代码中则可能不是.

In your first example, assuming that you haven't done so yourself, the word foo was not added to the runtime library ahead of time. That means that foo wasn't bound to lib, and since it wasn't declared as a local word it wasn't bound to the local context either. So as a result, foo wasn't bound to anything at all. In your code that was an error, but in other code it might not be.

有一个隔离"选项可以更改模块导入内容的方式,使其成为隔离"模块.让我们在这里使用第二个示例:

There is an "isolate" option that changes the way that modules import stuff, making it an "isolated" module. Let's use your second example here:

REBOL [Type: 'module Options: [isolate]] set 'foo "Bar"

制作隔离模块时,模块中的每个单词(即使是嵌套代码)都将收集到模块的本地上下文中.在这种情况下,这意味着setfoo是本地单词.这些单词的初始值设置为创建模块时它们在lib中具有的任何值.也就是说,如果单词完全在lib中定义.如果单词在lib中没有值,则它们最初也不在模块中具有值.

When an isolated module is made, every word in the module, even in nested code, is collected into the module's local context. In this case, it means that set and foo are local words. The initial values of those words are set to whatever values they have in lib at the time the module is created. That is, if the words are defined in lib at all. If the words don't have values in lib, they won't initially have values in the module either.

重要的是要注意,这种导入值是一次性的事情.初始导入后,在模块外部对这些单词所做的任何更改都不会影响模块中的单词.这就是为什么我们说模块是隔离的".就您的代码示例而言,这意味着某人可以更改lib/set并且不会影响您的代码.

It is important to note that this import of values is a one-time thing. After that initial import, any changes to these words made outside the module don't affect the words in the module. That is why we say the module is "isolated". In the case of your code example, it means that someone could change lib/set and it wouldn't affect your code.

但是您错过了另一种重要的模块类型...

But there's another important module type you missed...

在Rebol 3中,脚本是另一种模块.这是您的脚本代码:

In Rebol 3, scripts are another kind of module. Here's your code as a script:

REBOL [] set 'foo "Bar"

或者,如果愿意,因为脚本标题在Rebol 3中是可选的,

Or if you like, since script headers are optional in Rebol 3:

set 'foo "Bar"

脚本还从lib导入单词,然后将它们导入到隔离的上下文中,但是要有所不同:所有脚本共享 same 隔离的上下文,称为用户"上下文.这意味着当您在脚本中更改单词的值时,使用该单词的 next 脚本将在单词开始时看到更改.因此,如果在运行上述脚本之后,尝试运行此脚本:

Scripts also import their words from lib, and they import them into an isolated context, but with a twist: All scripts share the same isolated context, known as the "user" context. This means that when you change the value of a word in a script, the next script to use that word will see the change when it starts. So if after running the above script, you try to run this one:

print foo

然后它将打印"Bar",而不是定义foo,即使foo仍未在lib中定义.您可能会发现,如果以交互方式使用Rebol 3,在控制台中输入命令并获得结果,则您输入的每个命令行都是单独的脚本,这可能会很有趣.因此,如果您的会话如下所示:

Then it will print "Bar", rather than have foo be undefined, even though foo is still not defined in lib. You might find it interesting to know that if you are using Rebol 3 interactively, entering commands into the console and getting results, that every command line you enter is a separate script. So if your session looks like this:

>> x: 1
== 1
>> print x
1

x: 1print x行是单独的脚本,第二行利用了第一行对用户上下文所做的更改.

The x: 1 and print x lines are separate scripts, the second taking advantage of the changes made to the user context by the first.

用户上下文实际上应该是任务本地的,但是暂时让我们忽略它.

The user context is actually supposed to be task-local, but for the moment let's ignore that.

这是我们回到系统功能"的地方,而Rebol没有它们. set函数与其他任何函数一样.它的实现方式可能有所不同,但是仍然是分配给普通单词的普通值.应用程序必须管理这些词的 lot ,因此这就是我们拥有模块和运行时库的原因.

Here is where we get back to the "system function" thing, and that Rebol doesn't have them. The set function is just like any other function. It might be implemented differently, but it's still a normal value assigned to a normal word. An application will have to manage a lot of these words, so that's why we have modules and the runtime library.

在应用程序中,有些东西需要更改,而其他东西则需要更改,而哪些东西取决于应用程序.您将需要对您的内容进行分组,保持内容井然有序或进行访问控制.将存在全局定义的物料和本地定义的物料,并且您将需要一种有组织的方法来将全局物料运到本地,反之亦然,并解决不止一个项目想要用以下方法定义物料时的所有冲突.相同的名字.

In an application there will be stuff that needs to change, and other stuff that needs to not change, and which stuff is which depends on the application. You will want to group your stuff, to keep things organized or for access control. There will be globally defined stuff, and locally defined stuff, and you will want to have an organized way to get the global stuff to the local places, and vice-versa, and resolve any conflicts when more than one thing wants to define stuff with the same name.

在Rebol 3中,为了方便和访问控制,我们使用模块对内容进行分组.我们使用运行时库lib作为收集模块导出并解决冲突的地方,以便控制将哪些内容导入到本地位置,如其他模块和用户上下文.如果需要覆盖某些内容,可以通过更改运行时库,并在必要时将更改传播到用户上下文中来进行.您甚至可以在运行时升级模块,并使模块的新版本覆盖旧版本导出的单词.

In Rebol 3, we use modules to group stuff, for convenience and access control. We use the runtime library lib as a place to collect the exports of the modules, and resolve conflicts, in order to control what gets imported to the local places like other modules and the user context(s). If you need to override some stuff, you do this by changing the runtime library, and if necessary propagating your changes out to the user context(s). You can even upgrade modules at runtime, and have the new version of the module override the words exported by the old version.

对于常规模块,当事物被覆盖或升级时,您的模块将受益于此类更改.假设这些更改是有好处的,那么这可能是一件好事.常规模块与其他常规模块和脚本配合使用,以使共享环境可以工作.

For regular modules, when things are overridden or upgraded, your module will benefit from such changes. Assuming those changes are a benefit, this can be a good thing. A regular module cooperates with other regular modules and scripts to make a shared environment to work in.

但是,有时您需要与这些更改分开.也许您需要某些功能的特定版本,并且不想升级.也许您的模块将在不太值得信赖的环境中加载,并且您不希望您的代码被黑客入侵.也许您只需要使事情更加可预测即可.在这种情况下,您可能希望将模块与这些外部更改隔离开来.

However, sometimes you need to stay separate from these kinds of changes. Perhaps you need a particular version of some function and don't want to be upgraded. Perhaps your module will be loaded in a less trustworthy environment and you don't want your code hacked. Perhaps you just need things to be more predictable. In cases like this, you may want to isolate your module from these kinds of external changes.

被隔离的不利之处在于,如果您想要对运行时库进行更改,则不会得到它们.如果您的模块可以通过某种方式访问​​(例如通过导入名称),则有人可以将这些更改传播给您,但是如果您无法访问,那么您就不走运了.希望您已经考虑过监视lib所需的更改,或者直接通过lib引用这些内容.

The downside to being isolated is that, if there are changes to the runtime library that you might want, you're not going to get them. If your module is somehow accessible (such as by having been imported with a name), someone might be able to propagate those changes to you, but if you're not accessible then you're out of luck. Hopefully you've thought to monitor lib for changes you want, or reference the stuff through lib directly.

仍然,我们错过了另一个重要问题...

Still, we've missed another important issue...

管理运行时库和所有这些本地上下文的另一部分正在导出.您必须以某种方式将您的东西拿出来.最重要的因素是您不会怀疑的事情:您的模块是否有名称.

The other part of managing the runtime library and all of these local contexts is exporting. You have to get your stuff out there somehow. And the most important factor is something that you wouldn't suspect: whether or not your module has a name.

名称对于Rebol 3的模块是可选的.最初,这似乎只是使编写模块变得更简单的一种方法(而在卡尔的原始建议中,这正是原因).但是,事实证明,当您有一个名字时,您可以做很多事情,而当您没有一个名字时,您不能做,这仅仅是因为名字是什么:一种引用某物的方法.如果您没有名字,就没有办法指代某物.

Names are optional for Rebol 3's modules. At first this might seem like just a way to make it simpler to write modules (and in Carl's original proposal, that is exactly why). However, it turns out that there is a lot of stuff that you can do when you have a name that you can't when you don't, simply because of what a name is: a way to refer to something. If you don't have a name, you don't have a way to refer to something.

这似乎是一件微不足道的事情,但以下是一些名称可以让您做到的事情:

It might seem like a trivial thing, but here are some things that a name lets you do:

  • 您可以判断模块是否已加载.
  • 您可以确保模块仅加载一次.
  • 您可以判断模块是否有较早的版本,甚至可以对其进行升级.
  • 您可以访问之前加载的模块.

当Carl决定将名称设为可选时,他给我们带来了一种情况,那就是可以制作您无法做任何事情的模块.鉴于模块导出是要在运行时库中收集和组织的,因此我们遇到这样的情况,您可能会对无法轻松检测到的库产生影响,并且每次导入模块时都要重新加载模块.

When Carl decided to make names optional, he gave us a situation where it would be possible to make modules for which you couldn't do any of those things. Given that module exports were intended to be collected and organized in the runtime library, we had a situation where you could have effects on the library that you couldn't easily detect, and modules that got reloaded every time they were imported.

因此,为了安全起见,我们决定完全切掉运行时库,只将这些未命名模块中的单词直接导出到要导入它们的本地(模块或用户)上下文中.这使得这些模块实际上是私有的,就像它们是目标上下文所拥有的一样.我们采取了一个可能很尴尬的情况,并将其作为功能.

So for safety we decided to cut out the runtime library completely and just export words from these unnamed modules directly to the local (module or user) contexts that were importing them. This makes these modules effectively private, as if they are owned by the target contexts. We took a potentially awkward situation and made it a feature.

此功能如此,我们决定使用private选项显式支持它.将其设为显式选项可以帮助我们解决没有名称引起的最后一个问题:使专用模块不必一遍又一遍地重新加载.如果给模块命名,则其导出仍可以是私有的,但只需要导出一个副本即可.

It was such a feature that we decided to support it explicitly with a private option. Making this an explicit option helps us deal with the last problem not having a name caused us: making private modules not have to reload over and over again. If you give a module a name, its exports can still be private, but it only needs one copy of what it's exporting.

但是,不管是否命名,是否私有,这是3种导出类型.

However, named or not, private or not, that is 3 export types.

让我们来学习这个模块:

Let's take this module:

REBOL [type: module name: foo] export bar: 1

导入此命令会将模块添加到已加载的模块列表中,默认版本为0.0.0,并将一个单词bar导出到运行时库.在这种情况下,导出"表示将单词bar添加到运行时库(如果不存在),并将该单词lib/bar设置为在foo完成执行后该单词foo/bar具有的值(如果尚未设置).

Importing this adds a module to the loaded modules list, with the default version of 0.0.0, and exports one word bar to the runtime library. "Exporting" in this case means adding a word bar to the runtime library if it isn't there, and setting that word lib/bar to the value that the word foo/bar has after foo has finished executing (if it isn't set already).

值得注意的是,当foo的主体完成执行时,此自动导出仅发生一次.如果之后对foo/bar进行更改,则不会影响lib/bar.如果您也想更改lib/bar,则必须手动进行.

It is worth noting that this automatic exporting happens only once, when the body of foo is finished executing. If you make a change to foo/bar after that, that doesn't affect lib/bar. If you want to change lib/bar too, you have to do it manually.

还值得注意的是,如果在导入foo之前已经存在lib/bar,则不会再添加其他单词.如果lib/bar已经设置为一个值(未设置),则导入foo不会覆盖现有值.先到先得.如果要覆盖现有的lib/bar值,则必须手动进行.这就是我们使用lib来管理替代项的方式.

It is also worth noting that if lib/bar already exists before foo is imported, you won't have another word added. And if lib/bar is already set to a value (not unset), importing foo won't overwrite the existing value. First come, first served. If you want to override an existing value of lib/bar, you'll have to do so manually. This is how we use lib to manage overrides.

运行时库给我们的主要优点是,我们可以在一个地方管理所有导出的单词,从而解决冲突和替代.但是,另一个优点是,大多数模块和脚本实际上不必说出它们要导入的内容.只要在运行时库中预先正确填充了所需的所有单词,以后再加载的脚本或模块就可以了.这样可以很容易在启动代码中放置一堆导入语句和任何替代内容,从而设置了其余代码所需要的一切.目的是使组织和编写应用程序代码更加容易.

The main advantages that the runtime library gives us is that we can manage all of our exported words in one place, resolving conflicts and overrides. However, another advantage is that most modules and scripts don't actually have to say what they are importing. As long as the runtime library is filled in properly ahead of time with all the words you need, your script or module that you load later will be fine. This makes it easy to put a bunch of import statements and any overrides in your startup code which sets up everything the rest of your code will need. This is intended to make it easier to organize and write your application code.

在某些情况下,您不想将内容导出到主运行时库. lib中的内容会导入到所有内容中,因此您只应将要使其普遍可用的内容导出到lib中.有时您想要制作仅导出所需内容的模块.有时您有一些相关的模块,一个通用工具和一个实用程序模块等等.在这种情况下,您可能想要创建一个私有模块.

In some cases, you don't want to export your stuff to the main runtime library. Stuff in lib gets imported into everything, so you should only export stuff to lib that you want to make generally available. Sometimes you want to make modules that only export stuff for the contexts that want it. Sometimes you have some related modules, a general facility and a utility module or so. If this is the case, you might want to make a private module.

让我们来学习这个模块:

Let's take this module:

REBOL [type: module name: foo options: [private]] export bar: 1

导入此模块不会影响lib.而是将其导出收集到私有运行时库中,该库对于导入该模块的模块或用户上下文是本地的,以及目标要导入的任何其他私有模块的库,然后从那里导入到目标.专用运行时库用于与lib相同的冲突解决方案.主运行时库lib优先于私有库,因此不要指望私有库覆盖全局事物.

Importing this module doesn't affect lib. Instead, its exports are collected into a private runtime library that is local to the module or user context that is importing this module, along with those of any other private modules that the target is importing, then imported to the target from there. The private runtime library is used for the same conflict resolution that lib is used for. The main runtime library lib takes precedence over the private lib, so don't count on the private lib overriding global things.

这种东西对于制作实用程序模块,高级API或其他类似技巧很有用.如果需要的话,这对于制作需要显式导入的强模块化代码也很有用.

This kind of thing is useful for making utility modules, advanced APIs, or other such tricks. It is also useful for making strong-modular code which requires explicit imports, if that is what you're into.

值得注意的是,如果您的模块实际上没有导出任何内容,则命名的私有模块或命名的公共模块之间没有区别,因此基本上将其视为公共模块.重要的是它有一个名字.这带我们去...

It's worth noting that if your module doesn't actually export anything, there is no difference between a named private module or a named public module, so it's basically treated as public. All that matters is that it has a name. Which brings us to...

如上所述,如果您的模块没有名称,则几乎必须将其视为私有.但是,它比私有的要多得多,因为您无法确定它是否已加载,因此无法升级它,甚至无法重新加载它.但是,如果那是您想要的呢?

As explained above, if your module doesn't have a name then it pretty much has to be treated as private. More than private though, since you can't tell if it's loaded, you can't upgrade it or even keep from reloading it. But what if that's what you want?

在某些情况下,您确实希望代码能够生效.在这些情况下,您想要执行的是每次都重新运行代码.可能是您正在使用do运行的脚本,但是将其构造为模块以避免泄漏单词.也许您正在做一个混合,一些实用程序函数具有一些需要初始化的局部状态.可能几乎什么都可以.

In some cases, you really want your code run for effect. In these cases having your code rerun every time is what you want to do. Maybe it's a script that you're running with do but structuring as a module to avoid leaking words. Maybe you're making a mixin, some utility functions that have some local state that needs initializing. It could be just about anything.

我经常使我的%rebol.r文件成为一个未命名的模块,因为我想对其输出内容以及输出方式进行更多控制.另外,由于它是为效果而做的,不需要重新加载或升级,因此没有必要为其命名.

I frequently make my %rebol.r file an unnamed module because I want to have more control over what it exports and how. Plus, since it's done for effect and doesn't need to be reloaded or upgraded there's no point in giving it a name.

不需要代码示例,您以前的示例将以这种方式进行操作.

No need for a code example, your earlier ones will act this way.

我希望这能使您对R3的模块系统的设计有足够的了解.

这篇关于Rebol模块中的单词是如何绑定的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆