为什么Erlang为模块提供'import',为头文件提供`include`?

s6fujrry  于 2022-12-08  发布在  Erlang
关注(0)|答案(3)|浏览(262)

Erlang的-import()指令允许你从其他模块导入代码,它的include()指令允许你从头文件导入代码。为什么有理由选择其中一个呢?
我的直觉是,当您不希望必须限定
一些Erlang声明[1]:“Erlang头文件与它们的C对应部分非常相似:它们只不过是添加到模块中的一小段代码,就好像它是一开始就写在那里一样。”因此,包含似乎会导致编译器在不同的模块中重复工作。而头文件似乎是强制模块系统之上的一个可选复杂因素。那么,我为什么要使用头文件呢?
[1][https://learnyousomeerlang.com/a-short-visit-to-common-data-structures](https://learnyousomeerlang.com/a-short-visit-to-common-data-structures)

vhmi4jdf

vhmi4jdf1#

Erlang的-import只允许你调用导入的函数,而没有Module。它损害了可读性,不应该使用:您需要检查import指令以了解函数是模块的本地函数还是外部函数。
使用头文件,您可以获得与C相同的功能,您可以使用它们来共享-record定义,而不是拥有类似dto的模块(1),您可以使用它们来包含-define以使用相同的宏(2)。
一句话:

-record(position, {x, y}).

假设您在整个代码中使用#position{},而不是在任何地方定义记录并在记录定义更改时更新所有副本,您使用了一个头(或一个具有不透明类型的dto模块,但这是另一个问题)。
我们希望你记得更新所有的副本,否则混乱接踵而至。
二:

-define(ENUM01, enum01).
-define(DEFAULT_TIMEOUT, 1000).

在头文件中定义它们并将它们用作?ENUM01?DEFAULT_TIMEOUT,而不是在任何地方都使用enum011000,因为这样容易出错,并且如果需要更改它们,需要多次更新
或者您可以在测试时更彻底:

-ifdef(TEST).
-define(assert(A), true = A).
-else
-define(assert(A), A).
-endif.

或者,您可以包含一些有用的信息:

-define(LOG(Level, X), logger:log(Level, X, #{line => ?LINE}).
kqlmhetl

kqlmhetl2#

Erlang标准库使用头文件来提供向代码添加元数据的能力。
例如,EUnit功能:

-include_lib("eunit/include/eunit.hrl").
5jvtdoz2

5jvtdoz23#

import有助于构建封装,而include是一种预处理(这意味着代码在通过编译器之前将成为单元的一部分)。
一个import在两个模块之间产生了依赖关系,这意味着一个模块A导入模块B * 有 * B作为依赖关系...而一个include是扩展的,这意味着一个模块 * 包含 * 一些代码,而这些代码 * 是 * 模块本身的一部分,这就是头文件的作用。
module(s)和header(s)是两个语义不同的东西,服务于不同的目的。(s),通过不导出来限制内容,从其他模块导入(但默认情况下不导出),重新导出导入的内容等。因此,当我们import内容时,我们可以调用其他模块中的函数,而只是那些在其他模块中导出的文件。但是header文件不是这样的。header文件中的所有内容都成为包含它们的模块的一部分。头文件中没有导出/导入的意义。头文件对于编写和分发定义非常有用,否则在大型程序中会导致冗余。
所以本质上他们是两个不同的东西,所以两个不同的关键词可供我们处置。所以不要喜欢一个比另一个。学习他们两个,因为我们需要他们两个。

相关问题