【问题标题】:For a Common Lisp library made of multiple packages, is there a standard way to export the API?对于由多个包组成的 Common Lisp 库,是否有标准的方法来导出 API?
【发布时间】:2012-10-08 14:55:28
【问题描述】:

显然,外部可见的 API 是通过导出符号来发布的。但是...如果我有多个包(比如 A、B 和 C)并且 A 的导出符号并不都意味着是外部 API 的一部分——B 和 C 需要其中一些? (类似地,B 为 A 和 C 导出一些符号,为外部 API 导出一些符号;C 是“顶级”包,它所有导出的符号都是公共 API 的一部分;我想保持模块化并允许 A 隐藏其内部来自 B 和 C,所以我避免使用 '::')。

我现在的解决方案是从 C 中重新导出所有应该公开的内容,并记录公共 API 仅包含 C 的导出符号,并且人们应该远离 A 和 B 的公共符号,以免出现错误和将来内部接口更改时代码会损坏。

有没有更好的办法?

更新:这是我对 Xach 答案的理解的实现:

首先,让我完成我的示例。我想从包a 导出符号symbol-a-1symbol-a-2,从包b 导出符号symbol-b-1symbol-b-2,从包c 导出符号api-symbol-1api-symbol-2。只有从 c 导出的符号是公共 API 的一部分。

首先,a的定义:

(defpackage #:a
  (:use #:cl))

请注意,没有任何导出符号 :-)

辅助宏(使用 Alexandria):

(defmacro privately-export (package-name &body symbols)
  `(eval-when (:compile-toplevel :load-toplevel :execute)
     (defun ,(alexandria:format-symbol *package*
                                       "IMPORT-FROM-~a"
                                       (symbol-name package-name)) ()
       (list :import-from
             ,package-name
             ,@(mapcar (lambda (to-intern)
                         `',(intern (symbol-name to-intern) package-name))
                       symbols)))))

使用宏“私下导出”:-) :

(privately-export :a :symbol-a-1 :symbol-a-2)

现在b的定义:

(defpackage #:b
  (:use #:cl)
  #.(import-from-a))

...b 的“出口”:

(privately-export :b :symbol-b-1 :symbol-b-2)

...c的定义:

(defpackage #:c
  (:use #:cl)
  #.(import-from-a)
  #.(import-from-b)
  (:export :api-symbol-1 :api-symbol-2)

这种方法的问题:

  • a 不能使用来自b 的符号(在两者都已定义之后,没有来自bb 的符号a);
  • 语法package:symbol 基本上不能用于“私人”导出的符号(它要么只是symbol 要么package::symbol)。

【问题讨论】:

  • 嗯。三个很好的答案,很难选择答案。 :-)

标签: common-lisp


【解决方案1】:

如果 A 和 B 主要用于 C 的实现,您可以让 C 的 defpackage 形式驱动事物,并选择性地使用 :import-from,因为您可以导入非外部的事物。然后您可以有选择地从那里重新导出。

【讨论】:

  • 我错过了:import-from 可以与非外部符号一起使用的事实。但是,更新我的代码以使用:import-from 而不是:use 意味着我必须在每个“导入器”包中复制导入包的:export 列表。我错过了什么吗?关于将:used 包转换为:import-from 包并排除:import-from 符号列表的任何建议?谢谢!
  • 我根据我对您的回答的理解更新了这个问题,我很想听听改进或更正。再次感谢!
【解决方案2】:

您可以添加第三个包 D,它导出所有公共 API 符号,并将 A、B 和 C 包视为私有包。然后,您可以使用限定名称来限定 API 包中的所有函数和变量定义,例如 in

(defun D:blah () ...)

便于直观地发现公共入口点的定义。

【讨论】:

  • 这基本上就是我现在所做的,我的 C 是你的 D。限定公共符号定义的建议是值得深思的。谢谢!
【解决方案3】:

大概,最简单的方法是Hans提出的。

你也可以看看 Tim Bradshaw 的Conduit packages

【讨论】:

  • 我会研究“导管包”。谢谢!
猜你喜欢
  • 2012-11-06
  • 1970-01-01
  • 2016-02-24
  • 1970-01-01
  • 2022-01-04
  • 2012-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多