在模块(分发?)级别强制执行API边界
我如何构建 Raku 代码,以便某些符号在我正在编写的库中是公开的,但不对库的用户公开?(我说“库”是为了避免使用术语“分发”和“模块”,文档有时会以重叠的方式使用它们。但如果我应该使用更精确的术语,请告诉我。)
我了解如何在单个文件中控制隐私。例如,我可能有一个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,那么我将需要从模块中给它our(即包)范围和export它Bar,以便能够use从Foo. 但以同样的方式我出口这样&public的Foo会导致我的库的用户能够use Foo和呼叫&private-也就是结局我想避免的。如何维护&private的隐私?
(我通过在 META6.json 文件中将Foo我的分发版provides列为一个模块来研究强制执行隐私。但从文档中,我的理解是,provides默认情况下控制哪些模块包管理器像 zef install 但实际上并不控制代码的隐私。 那是对的吗?)
[编辑:我得到的前几个回复让我怀疑我是否遇到了XY 问题。我以为我问的是“简单的事情应该很容易”类别中的某些内容。我正在解决从 Rust 背景强制执行 API 边界的问题,其中常见的做法是将模块在 crate 中公开(或仅公开给它们的父模块)——这就是 XI 所问的问题。但是,如果有更好/不同的方式来强制 Raku 中的 API 边界,我也会对该解决方案感兴趣(因为这是 YI 真正关心的)]
回答
我需要给它我们的(即包)范围并从 Bar 模块导出它
第一步不是必须的。该export机制也适用于词法范围的subs,这意味着它们仅可用于导入它们的模块。由于没有隐式重新导出,模块用户必须显式使用包含实现细节的模块才能获得它们。(our顺便说一句,就我个人而言,我几乎从不使用模块中的 subs 范围,并且完全依赖于导出。但是,我明白为什么人们也可能决定以完全限定的名称提供它们。)
也可以对内部事物使用导出标签(is export(:INTERNAL), 然后use My::Module::Internals :INTERNAL),以向模块用户提供更强有力的提示,即他们正在使保修无效。归根结底,无论语言提供什么,有足够决心重用内部结构的人都会找到一种方法(即使它是从您的模块复制粘贴)。总的来说,Raku 的设计更侧重于让人们更容易做正确的事情,而不是让他们不可能“错误”的事情,如果他们真的想要的话,因为有时错误的事情仍然比其他选择的错误要少.
回答
Off the bat, there's very little you can't do, as long as you're in control of the meta-object protocol. Anything that's syntactically possible, you could in principle do it using a specific kind of method, or class, declared using that. For instance, you could have a private-class which would be visible only to members of the same namespace (to the level that you would design). There's Metamodel::Trusting which defines, for a particular entity, who it does trust (please bear in mind that this is part of the implementation, not spec, and then subject to change).
一种可扩展性较差的方法是使用trusts. 新的私有模块需要是类,并trusts X为每个访问它的类发出一个。这可能包括属于同一发行版的类……与否,这由您决定。正是上面的 Metamodel 类提供了这个特性,所以直接使用它可能会给你更高级别的控制(使用较低级别的编程)