如何通过 Erlang 的预处理器有条件地排除整个子句?

How to conditionally exclude whole clauses via Erlang's preprocessor?

我正在用 Erlang 编写一些模块,应该在各种 OTP 版本上编译。在某些情况下,我想使用旧版本中不受支持的 ++ 运算符模式,但前提是它可用。因此,我对这项任务的尝试如下:

f([]) -> empty;
f([_|_]) -> cons;
-if(?OTP_RELEASE >= 23).
f([] ++ _) -> empty_append;
-endif.
f(X) -> error(nah).

我找到的解决此问题的方法是:

有什么方便的方法吗?如果可能的话,我会很感激一个通用的解决方案,不限于目前的情况。

来自flow control in macrosThe macro directives cannot be used inside functions.所以如果你想使用-ifdef你需要复制多次相同的功能。

(我想这与它们被定义在 -. 之间有关)

话虽如此,您可以执行以下操作:

f([]) -> empty;
f([_|_]) -> cons;
f([] ++ _) when ?OTP_RELEASE >= 23 -> empty_append;
f(X) -> error(nah).

您可以使用 erlc -S <module> 验证永远不会匹配的子句已从生成的 beam ASM 代码中删除。此外,编译器将显示警告。

这一步产生的警告不能选择性省略。来自 here:

Another class of warnings is generated by the compiler during optimization and code generation. They warn about patterns that will never match (such as a=b), guards that always evaluate to false, and expressions that always fail (such as atom+42).

Those warnings cannot be disabled (except by disabling all warnings).

如果你想完全避免警告,你需要类似的东西(请注意,这种风格不会删除分支,因为 OtpRelease 是一个变量):

f(Term) ->
    f(Term, ?OTP_RELEASE).

f([], _) -> empty;
f([_|_], _) -> cons;
f([] ++ _, OtpRelease) when OtpRelease >= 23 -> empty_append;
f(_, _) -> error(nah).

编辑: 在最后一个示例中,OTP23 的代码优化器能够删除无法访问的代码。在这种特殊情况下,如果 f/2 未导出,则根据 ?OTP_RELEASE)

将删除其第 3 或第 4 个子句