传递给库元谓词的目标的模块扩展
Module expansion of goals passed to library meta-predicates
使用SWI-Prolog(多线程,64 位,版本 7.3.5),
我们一步步进行:
在模块dcgAux
中定义dcg非终结符a//1
(读作:“di-SEE-goh”) :
:- module(dcgAux,[a//1]).
a(0) --> [].
a(s(N)) --> [a], a(N).
运行 以下查询——使用 phrase/2
and apply:foldl/4
:
?- use_module([library(apply),dcgAux]).
true.
?- phrase( foldl( a,[s(0),s(s(0))]),[a,a,a]).
true.
?- phrase( foldl(dcgAux:a,[s(0),s(s(0))]),[a,a,a]).
true.
?- phrase(apply:foldl(dcgAux:a,[s(0),s(s(0))]),[a,a,a]).
true.
?- phrase(apply:foldl( a,[s(0),s(s(0))]),[a,a,a]).
ERROR: apply:foldl_/4: Undefined procedure: apply:a/3
нет! 相当惊喜——而且不是很好。我们是否遗漏了一些未知的未知数?
要摆脱上述恼人的行为,我们必须首先找出造成它的原因:
?- import_module(apply,M), M=user.
false.
?- phrase(apply:foldl(a,[s(0),s(s(0))]),[a,a,a]).
ERROR: apply:foldl_/4: Undefined procedure: apply:a/3
?- add_import_module(apply,user,end).
true.
?- import_module(apply,M), M=user. % sic!
M = user. % `?- import_module(apply,user).` fails!
?- phrase(apply:foldl(a,[s(0),s(s(0))]),[a,a,a]).
true.
这是怎么回事?我是这样看的:
- 传递给
foldl/4
的目标的模块扩展是有限的。
引自SWI-Prolog manual page on import_module/2
:
All normal modules only import from user, which imports from system.
SWI 的 library(apply)
只有 system
来自 system
,而不是 user
。
如果我们将模块 apply
克隆到 applY
(并传播新模块名称),我们观察到:
?- use_module(applY).
true.
?- phrase(applY:foldl(a,[s(0),s(s(0))]),[a,a,a]). % was: ERROR
true. % now: OK!
请分享您对我could/should如何进行的想法!
(我还没有 运行 与其他 Prolog 处理器进行类似的实验。)
这是 Quintus 传统中基于谓词的模块系统固有的 feature/bug。也就是说,这个模块系统首先是为Quintus Prolog开发的。它随后被 SICStus 采用(在 0.71 之后),然后(或多或少)被 13211-2,然后被 YAP,以及(经过一些修改)被 SWI 采用。
这里的问题是明确限定的确切含义。只要目标不是元谓词,事情就很容易解决:采用最内层限定的模块。然而,一旦你有了元谓词,元参数就需要被告知那个模块;或不。如果元参数被告知,我们说 冒号设置调用上下文,如果没有,则需要一些其他方法来实现该目的。
在 Quintus 传统中,会考虑元参数。结果你看到了。因此,您无法直接比较同一模块中同一元谓词的两个实现。还有其他方法最值得注意的是 IF and ECLiPSe,它们不会通过冒号更改调用上下文。这有优点也有缺点。最好是逐个比较。
这是最近的一个案例。以 lambdas 为例,以及它们是如何放入模块中的
在 SICStus 中,in SWI, and in ECLiPSe.
至于Quintus/SICStus/YAP/SWI模块系统,我宁愿以最保守的方式使用它。即:
没有明确的限定,将中缀 :
视为内部的东西
干净、可检查的元声明——故意插入一个未定义的谓词只是为了看看交叉引用是否能够检测到问题(在 SWI 中是 check
或 make
).
使用公共子集,避免花里胡哨的东西 - 有很多用心良苦的扩展...
以行人的方式做更多通用的事情:通过添加适当的模块重新导出并添加虚拟定义。同样,不是重命名,而是从接口模块导入东西。
请始终注意模块系统固有一些限制。无论您如何扭曲或转动它。没有完全无缝的模块系统,因为模块的目的就是分离代码和关注点。
1:准确地说,SICStus 对 Quintus 模块的改编在 meta_predicate
声明中仅包含 :
用于模块敏感参数。对于基于 call/N
的高阶编程非常重要的整数 0..9
仅在大约 20 年后的
4.2.0 released 2011-03-08.
使用SWI-Prolog(多线程,64 位,版本 7.3.5), 我们一步步进行:
在模块
dcgAux
中定义dcg非终结符a//1
(读作:“di-SEE-goh”) ::- module(dcgAux,[a//1]). a(0) --> []. a(s(N)) --> [a], a(N).
运行 以下查询——使用
phrase/2
andapply:foldl/4
:?- use_module([library(apply),dcgAux]). true. ?- phrase( foldl( a,[s(0),s(s(0))]),[a,a,a]). true. ?- phrase( foldl(dcgAux:a,[s(0),s(s(0))]),[a,a,a]). true. ?- phrase(apply:foldl(dcgAux:a,[s(0),s(s(0))]),[a,a,a]). true. ?- phrase(apply:foldl( a,[s(0),s(s(0))]),[a,a,a]). ERROR: apply:foldl_/4: Undefined procedure: apply:a/3
нет! 相当惊喜——而且不是很好。我们是否遗漏了一些未知的未知数?
要摆脱上述恼人的行为,我们必须首先找出造成它的原因:
?- import_module(apply,M), M=user. false. ?- phrase(apply:foldl(a,[s(0),s(s(0))]),[a,a,a]). ERROR: apply:foldl_/4: Undefined procedure: apply:a/3 ?- add_import_module(apply,user,end). true. ?- import_module(apply,M), M=user. % sic! M = user. % `?- import_module(apply,user).` fails! ?- phrase(apply:foldl(a,[s(0),s(s(0))]),[a,a,a]). true.
这是怎么回事?我是这样看的:
- 传递给
foldl/4
的目标的模块扩展是有限的。 引自SWI-Prolog manual page on
import_module/2
:All normal modules only import from user, which imports from system.
SWI 的
library(apply)
只有system
来自system
,而不是user
。如果我们将模块
apply
克隆到applY
(并传播新模块名称),我们观察到:?- use_module(applY). true. ?- phrase(applY:foldl(a,[s(0),s(s(0))]),[a,a,a]). % was: ERROR true. % now: OK!
请分享您对我could/should如何进行的想法!
(我还没有 运行 与其他 Prolog 处理器进行类似的实验。)
这是 Quintus 传统中基于谓词的模块系统固有的 feature/bug。也就是说,这个模块系统首先是为Quintus Prolog开发的。它随后被 SICStus 采用(在 0.71 之后),然后(或多或少)被 13211-2,然后被 YAP,以及(经过一些修改)被 SWI 采用。
这里的问题是明确限定的确切含义。只要目标不是元谓词,事情就很容易解决:采用最内层限定的模块。然而,一旦你有了元谓词,元参数就需要被告知那个模块;或不。如果元参数被告知,我们说 冒号设置调用上下文,如果没有,则需要一些其他方法来实现该目的。
在 Quintus 传统中,会考虑元参数。结果你看到了。因此,您无法直接比较同一模块中同一元谓词的两个实现。还有其他方法最值得注意的是 IF and ECLiPSe,它们不会通过冒号更改调用上下文。这有优点也有缺点。最好是逐个比较。
这是最近的一个案例。以 lambdas 为例,以及它们是如何放入模块中的 在 SICStus 中,in SWI, and in ECLiPSe.
至于Quintus/SICStus/YAP/SWI模块系统,我宁愿以最保守的方式使用它。即:
没有明确的限定,将中缀
:
视为内部的东西干净、可检查的元声明——故意插入一个未定义的谓词只是为了看看交叉引用是否能够检测到问题(在 SWI 中是
check
或make
).使用公共子集,避免花里胡哨的东西 - 有很多用心良苦的扩展...
以行人的方式做更多通用的事情:通过添加适当的模块重新导出并添加虚拟定义。同样,不是重命名,而是从接口模块导入东西。
请始终注意模块系统固有一些限制。无论您如何扭曲或转动它。没有完全无缝的模块系统,因为模块的目的就是分离代码和关注点。
1:准确地说,SICStus 对 Quintus 模块的改编在 meta_predicate
声明中仅包含 :
用于模块敏感参数。对于基于 call/N
的高阶编程非常重要的整数 0..9
仅在大约 20 年后的
4.2.0 released 2011-03-08.