Module 在模块(分发?)级别强制执行API边界
我如何构造Raku代码,使某些符号在我正在编写的库中是公共的,但对库的用户不是公共的?(我说“库”是为了避免“分发”和“模块”这两个术语,文档有时会以重叠的方式使用它们。但是如果有更准确的术语我应该使用,请告诉我。) 我了解如何在单个文件中控制隐私。例如,我可能有一个包含以下内容的文件Module 在模块(分发?)级别强制执行API边界,module,private,encapsulation,raku,rakudo,Module,Private,Encapsulation,Raku,Rakudo,我如何构造Raku代码,使某些符号在我正在编写的库中是公共的,但对库的用户不是公共的?(我说“库”是为了避免“分发”和“模块”这两个术语,文档有时会以重叠的方式使用它们。但是如果有更准确的术语我应该使用,请告诉我。) 我了解如何在单个文件中控制隐私。例如,我可能有一个包含以下内容的文件Foo.rakumod: unit module Foo; sub private($priv) { #`[do internal stuff] } our sub public($input) is expo
Foo.rakumod
:
unit module Foo;
sub private($priv) { #`[do internal stuff] }
our sub public($input) is export { #`[ code that calls &private ] }
通过此设置,&public
是我的库的公共API的一部分,但&private
不是–我可以在Foo
中调用它,但我的用户不能
如果&private
变得足够大,我想将其拆分为自己的文件,那么如何保持这种分离?如果我将&private
移动到Bar.rakumod
,那么我需要将我们的(即包)范围赋予它,并从Bar
模块中导出它,以便能够从Foo
使用它。但是,以我从Foo
导出&public
的相同方式,这样做将导致我的库的用户能够使用Foo
并调用&private
——这正是我试图避免的结果。如何维护&private
的隐私
(我将Foo
列为我在META6.json文件中的模块,以此来加强隐私。但从文档中,我的理解是提供了
控制像zef这样的包管理器默认安装的模块,但实际上并不控制代码的隐私。对吗?)
[编辑:我收到的最初几条回复让我怀疑自己是否遇到了类似的问题。我想我是在问“简单的事情应该是简单的”类别中的一些问题。我要问的是从生锈的背景中强制API边界的问题,即在板条箱中公开模块(或者只针对他们的父模块)——这就是我问的X。但是如果有更好的/不同的方法来在Raku中强制执行API边界,我也会对该解决方案感兴趣(因为这是我真正关心的Y)]一开始,只要你能控制,几乎没有什么你做不到的。任何在语法上可能的事情,原则上你都可以使用一种特定的方法或类来完成。例如,你可以拥有一个只对相同命名空间的成员可见的私有类(达到您将要设计的级别)。对于特定实体,有一个定义它信任谁(请记住,这是实现的一部分,而不是规范,然后可能会发生更改)
一种可伸缩性较差的方法是使用。新的私有模块需要是类,并为每个访问它的类发出信任X
。这可能包括属于同一发行版的类…或不属于同一发行版,这取决于您的决定。正是上面的元模型类提供了这种特性,因此直接使用它可能会导致我会让你有更高的控制水平(编程水平更低)
我需要给它我们的(即包)范围,并从Bar模块导出它
第一步是不必要的。导出
机制在词汇范围的子
上也同样有效,这意味着它们只对导入它们的模块可用。由于没有隐式的重新导出,模块用户必须显式地使用包含实现细节的模块才能访问它们。(顺便说一句,就我个人而言,我几乎从不在我的模块中使用sub的
作用域,完全依赖于导出。不过,我明白了为什么人们可能会决定以完全限定的名称提供它们。)
还可以对内部内容使用导出标记(是导出(:internal)
,然后使用My::Module::internal:internal
)向模块用户提供一个更强烈的提示,说明他们正在取消保修。最终,无论使用何种语言,有足够决心重复使用内部组件的人都会找到一种方法(即使是从您的模块复制粘贴)一般来说,Raku的设计更注重让人们更容易做正确的事情,而不是让人们不可能“犯错”如果他们真的想做的话,就做一些事情,因为有时候错误的事情仍然没有其他选择那么错误。正如其他人所说,没有办法100%地执行这一点。Raku只是为用户提供了太多的灵活性,让您能够完美地在外部隐藏实现细节,同时仍然在内部文件之间共享它们嗯
但是,您可以使用类似以下的结构:
# in Foo.rakumod
use Bar;
unit module Foo;
sub public($input) is export { #`[ code that calls &private ] }
当从Foo
的主线中声明的函数调用时,此函数将正常工作,但如果从其他地方调用,则会引发异常。(当然,用户可以捕获异常;如果您想防止异常,您可以退出
——但是,有决心的用户可以覆盖&*退出
处理程序!正如我所说,Raku为用户提供了很大的灵活性)
不幸的是,上面的代码有运行时成本,而且相当冗长。而且,如果您想从更多位置调用&private
,它会变得更加冗长。因此,在大多数情况下,最好将私有函数保留在同一个文件中–但此选项适用于需要时。我不相信您会这样做可以。要做的一件事可以是(a)将它们标记为是实现细节(这里的信号是龙)和(b)仅通过导出向sub提供包密钥,如,使用Foo::Secret:I-hered-under-that-Foo-Secret-is-designed-for-internal-use-only-and-agree-to-in-hold-
# In Bar.rakumod
unit module Bar;
sub private($priv) is export is implementation-detail {
unless callframe(1).code.?package.^name eq 'Foo' {
die '&private is a private function. Please use the public API in Foo.' }
#`[do internal stuff]
}