据我所知, module!
类型提供比受保护的命名空间更好的结构object!
或'use
功能。 如何在模块-I中必然注意到有关绑定的话一些错误的话:
REBOL [Type: 'module] set 'foo "Bar"
此外,如何雷博尔一个字区分本地的模块( 'foo
)和系统功能( 'set
)?
小更新,不久之后:
我看到有一个改变的结合方法开关:
REBOL [Type: 'module Options: [isolate]] set 'foo "Bar"
这是什么有什么不同? 有什么陷阱中存在默认使用这种方法吗?
OK,这将是一个有点棘手。
在雷博尔3有没有这样的事情作为系统字,还有刚刚的话。 有些话已被添加到运行时库lib
,并set
是这些词,这恰好有一个分配给它的功能之一。 模块导入从文字lib
,但什么是“进口”是指依赖于模块选择。 这可能是更棘手比你期待的,所以让我解释一下。
一般的模块
对于初学者来说,我过去是什么意思进口的“正规”的模块,那些没有指定任何选项。 让我们先从你的第一个模块:
REBOL [Type: 'module] set 'foo "Bar"
首先,你有一个错误的假设这里:这个词foo
是不是本地的模块,它只是同set
。 如果要定义foo
为本地字,你有,你有对象一样,用这个词作为一组字在顶层,这样使用相同的方法:
REBOL [Type: 'module] foo: "Bar"
之间的唯一区别foo
和set
是你没有出口或加字foo
到lib
呢。 当你没有声明为本地话的模块中引用的话,它必须从某处得到他们的价值观和/或绑定。 对于一般的模块,它结合了代码lib
,然后再覆盖,通过再结合代码模块的当地情况。 在本地环境中定义的任何话都会被绑定到它。 在当地的情况没有定义的任何话将保留其旧绑定,在这种情况下lib
。 这就是“进口”是指一般的模块。
在你的第一个例子,假设你没有这样做你自己,这个词foo
未添加到时间提前运行时库。 这意味着, foo
不绑定到lib
,并因为它不是作为一个局部字宣称它不绑定到当地的情况无论是。 因此,作为一个结果, foo
不绑定到任何东西。 在你的代码,这是一个错误,但在其他代码也可能不是。
隔离模块
有改变的模块进口的东西,使之成为一个“独立”模块的方式是“隔离”选项。 让我们用你的第二个例子在这里:
REBOL [Type: 'module Options: [isolate]] set 'foo "Bar"
当分离的模块制成,模块中的每一个字,甚至在嵌套的代码,被收集到模块的本地环境。 在这种情况下,这意味着set
和foo
是本地话。 这些词语的初始值被设定为它们在任何值lib
在创建模块的时间。 也就是说,如果单词定义在lib
的。 如果也就是说不必在值lib
,他们不会初步具备模块中的任一值。
需要注意的是这种进口值的是一次性的事情是很重要的。 该初始导入后,在模块外做这些词的任何更改不影响模块中的话。 这就是为什么我们说模块“隔离”。 在你的代码示例的情况下,这意味着有人可以改变lib/set
,它不会影响你的代码。
但是,还有你错过了另一个重要的模块类型...
脚本
在雷博尔3,脚本是另一种模块。 这是你的代码的脚本:
REBOL [] set 'foo "Bar"
或者,如果你喜欢,因为脚本头在雷博尔3是可选的:
set 'foo "Bar"
脚本也导入他们的话,从lib
,他们将其导入到一个孤立的背景下,但与一捻:所有脚本共享同一个孤立的背景下,被称为“用户”上下文。 这意味着,当你在一个脚本改变一个字的值, 下一个脚本来使用这个词将会看到的变化在启动时。 因此,如果运行上面的脚本后,您尝试运行这个:
print foo
然后,它会打印出“酒吧”,而不是让foo
是不确定的,即使foo
仍然没有定义lib
。 你会觉得很有趣知道,如果你正在使用雷博尔3交互,输入命令到控制台,并得到结果,你输入的每一个命令行是一个单独的脚本。 所以,如果您的会话看起来是这样的:
>> x: 1
== 1
>> print x
1
所述x: 1
和print x
线是独立的脚本,由第一至用户上下文进行的更改第二趁势。
用户上下文实际上应该是任务的地方,但目前让我们忽略。
为什么会有差别?
这里我们回到“系统功能”的事情,那雷博尔没有他们。 该set
功能就像任何其他功能。 它可能被不同地实现,但它仍然分为正常字正常值。 应用程序必须管理大量的这些话,所以这就是为什么我们有模块和运行时库。
在实际应用中会出现的东西需要改变,需要不改变其他的东西,以及东西是依赖于应用程序。 你会希望将你的东西,以保持组织或用于访问控制的事情。 还有将在全球范围定义的东西,和本地定义的东西,你会希望有一个有组织的方式来获得全球的东西给当地的地方,反之亦然,并解决任何冲突时超过一两件事要定义的东西用相同的名称。
在雷博尔3中,我们使用的模块组的东西,为了方便和访问控制。 我们使用运行时库lib
作为一个地方收集模块的出口,化解矛盾,以控制哪些数据导入到本地的地方,如其他模块和用户上下文(S)。 如果你需要重写一些东西,你通过改变运行时库做到这一点,并在必要时传播更改了用户上下文(S)。 你甚至可以在运行时升级模块,并具有该模块的新版本会覆盖旧版本导出的话。
对于一般的模块,当事情都将被覆盖或升级,你的模块将受益于这样的变化。 假设这些变化都受益,这可能是一件好事。 定期模块与其它常规模块和脚本合作,使一个共享的环境中工作。
不过,有时你需要从这种变化中保持分离。 也许你需要一些功能的特定版本并且不想升级。 也许你的模块将在一个不可信赖的环境中加载,你不希望你的代码被黑客入侵。 也许你只需要的东西要更容易预测。 在这样的情况下,你可能希望你的模块从这些类型的外部变化隔离。
该缺点被孤立的是,如果有变化,你可能想在运行时库,你不会得到它们。 如果您的模块是莫名其妙地访问(例如具有被导入了名字),有人也许能够对这些变化传播给你,但如果你无法访问,那么你的运气了。 希望你曾想过监控lib
您要更改,或者通过引用的东西lib
直接。
尽管如此,我们已经错过了另一个重要的问题...
出口
管理运行时库和所有这些当地的情况是出口的另一部分。 你必须让你的东西在那里不知何故。 而最重要的因素是什么,你不会怀疑:你的模块是否有一个名字。
名称是可选的雷博尔3的模块。 起初,这似乎是只是一种更简单地写模块(和卡尔原来的提议,这也正是为什么)。 然而,事实证明,有很多东西,当你有一个名字,你不能当你不这样做,什么仅仅是因为一个名字,你可以这样做:一种方式是指什么。 如果你没有一个名字,你就没有办法来指代的东西。
这似乎是一件微不足道的小事,但这里有一些东西,一个名字让你做:
- 你可以告诉一个模块是否被加载。
- 您可以确保一个模块只装载一次。
- 你可以告诉一个模块的旧版本是否有更早,也许升级。
- 你可以访问该早先加载的模块。
当卡尔决定让名可选的,他给我们的情况下,将有可能使模块,你不能做任何的那些东西。 由于模块输出的目的是要收集,并在运行时库组织的,我们有一个情况下,你可能对你不容易察觉,和模块得到重新加载每次他们被进口时库的影响。
所以为了安全,我们决定彻底断绝了运行时库,只是出口从这些不愿透露姓名的模块话直接向当地(模块或用户)上下文的被导入它们。 这使得这些模块有效地私有,因为如果他们被目标环境所拥有。 我们采取了一个潜在的尴尬局面,并使其功能。
也正是这样一个功能,我们决定用一个明确支持private
选项。 使之成为一个明确的选项,可以帮助我们应对没有一个名字引起了我们的最后一个问题:使私人模块不必重装一遍又一遍。 如果你给一个模块的名称,它的出口仍然可以是私有的,但它只需要的就是它的出口一个副本。
然而,命名与否,私人与否,这是3种导出类型。
定期开展评选模块
让我们这个模块:
REBOL [type: module name: foo] export bar: 1
导入这增加了模块的加载的模块列表,以及0.0.0默认版本,并出口一个字bar
的运行时库。 “导出”,在这种情况下,意味着将字bar
的运行时库,如果它不存在,并且该字设定lib/bar
到该字中的值foo/bar
已之后foo
执行完成(如果它不是设定的话)。
值得注意的是,这种自动出口只发生过一次,当时的体foo
执行完毕。 如果您进行了更改foo/bar
后,不影响lib/bar
。 如果你想改变lib/bar
太,你不得不做手工。
还值得一提的是,如果lib/bar
前已经存在foo
是进口的,你不会有增加了一个字。 如果lib/bar
已被设置为一个值(未未设置),进口foo
不会覆盖现有的值。 先到先得。 如果你想覆盖的现有价值lib/bar
,你就必须手动进行。 这是我们如何使用lib
管理覆盖。
该运行时库为我们提供的主要优点是,我们可以管理我们所有的出口的话在一个地方,解决冲突和覆盖。 然而,另一个优势是,大部分模块和脚本实际上并没有说他们是什么进口。 只要运行时库正确的时间提前了所有你需要的话填写,您的脚本或模块,您稍后加载的将被罚款。 这可以很容易地把一堆import语句,并在你的启动代码设置了一切你的代码的其余部分将需要的任何覆盖。 这是为了更容易地组织和编写应用程序代码。
名称的私有模块
在某些情况下,你不希望你的东西出口到主要的运行时库。 东西在lib
被导入到的一切,所以你应该只出口的东西lib
要做出全面上市。 有时候,你想,只有导出工具和希望它的上下文模块。 有时候,你有一些相关的模块,一般的设施和公用模块或左右。 如果是这样的话,你可能要做出一个私人模块。
让我们这个模块:
REBOL [type: module name: foo options: [private]] export bar: 1
导入此模块不会影响lib
。 相反,它的出口被收集到一个私人的运行时库是本地被导入此模块,与那些目标被导入任何其他私人的模块,然后导入到目标从那里沿着模块或用户上下文。 私人运行时库用于相同的冲突解决该lib
的用途。 主要的运行时库lib
优先于私人的lib,所以不要对私营LIB压倒一切的全球事情算。
这种事情是制作实用模块,先进的API,或其他类似的技巧很有用。 这也是使这需要明确的进口,如果这是你感兴趣的事强,模块化的代码非常有用。
值得一提的是,如果你的模块实际上并不出口任何东西,有一个名为私有模块或命名公共模块之间没有区别,所以它基本上被视为公众。 所有重要的是,它有一个名字。 这给我们带来...
无名模块
如上所述,如果你的模块没有一个名字,那么它几乎已被视为私人。 超过私人虽然,因为如果它的加载,你看不出来,你不能升级,甚至从重装来保持。 但是,如果这就是你想要什么?
在某些情况下,你真的想为影响你的代码运行。 在这种情况下让你的代码重新运行每次都是你想要做什么。 也许这是你与运行脚本do
,但结构作为一个模块,以避免泄漏的话。 也许你正在做一个mixin,有需要初始化一些地方的国家的一些实用功能。 它可以是任何东西。
我经常让我的%rebol.r
文件未命名的模块,因为我想有过什么出口,以及如何更好地控制。 此外,因为它是影响完成,并不需要重新加载或升级有一个在给它起名字没有任何意义。
无需代码示例,你刚才的将这样的行为。
我希望这给你足够的R3的模块系统设计的概述。