选角为"any()"是让Dialyzer接受ETS匹配模式的好解决方案吗



转换为any()是让Dialyzer接受ETS匹配模式的好解决方案吗?

Dialyzer和match规范不能很好地配合使用,而且似乎没有标准的解决方案:

  • http://erlang.org/pipermail/erlang-questions/2012-June/067581.html
  • https://bugs.erlang.org/browse/ERL-892

以下是我正在考虑的解决方案的完整示例。如果最后一行的matcher('_')更改为"_",则Dialyzer会抱怨记录构造不好,但使用matcher/1功能似乎一切都很好:


-module(sample).
-record(rec, {field :: number}).
-export([main/1]).
-type matchvar() :: '$1' | '$2' | '$3' | '$4' | '$5' | '$6' | '$7' | '$8' | '$9' | '$10' | '$11' | '$12' | '$13' | '$14' | '$15' | '$16' | '$17' | '$18' | '$19' | '$20' | '$21' | '$22'.

-spec matcher('_' | matchvar()) -> any().
matcher(X) ->
case node() of
'$ will never match' -> binary_to_term(<<>>);
_ -> X
end.

main(_Args) ->
ets:match('my_table', #rec{field = matcher('$1')}, 1).

这是因为Dialyzer不能静态地判断matcher/1的不可访问的第一子句是不可访问。由于binary_to_term/1返回any(),Dialyzer推断matcher/1的返回类型为any()

这个技巧是让Dialyzer在使用火柴规格时保持快乐的好方法吗?通过";好,";我的意思是:

  • 运行时成本低
  • 少数步兵
  • 没有比这更好(更安全、更快、更符合人体工程学(的方法了

我看了node()的实现,认为它只是一个指针解引用,所以成本应该很低。"$永远不会匹配"实际上永远不会匹配,因为node()总是返回一个包含@的原子。但必须有更好的方法。

这里确实有两个问题,我结合起来避免了XY问题:

  1. 以上技术是让Dialyzer将某些东西处理为any()的好方法吗
  2. 让Dialyzer将matcher('_')视为any()是处理匹配规格的好解决方案吗

我不认为这是一个好的解决方案,因为你在编译时做了无用的工作(无论多么小(来满足一些东西,而且这样做会欺骗透析器。

当出现这种情况时,我通常会扩展记录以包括匹配变量并使用它(通常我的记录是-opaque,因此字段类型在构造函数中受到控制(。

您总是可以只导出实际类型的子类型,而不是使用-opaque(详细介绍了Pierre Krafft在ERL-892中的评论(:

-module(sample).
-record(rec, {field :: number() | '_'}).
-type rec() :: #rec{field :: number()}.
-export_type([rec/0]).
-export([main/1]).
-spec main(rec()) -> {[[rec()]], ets:continuation()} | '$end_of_table'.
main(Rec) ->
ets:match('my_table', Rec#rec{field = '_'}, 1).
-module(sample_user).
-export([main_ok/0, main_error/0]).
main_ok() ->
sample:main({rec, 1}).
main_error() ->
sample:main({rec, '_'}).
sample_user.erl
7: Function main_error/0 has no local return
8: The call sample:main({'rec', '_'}) breaks the contract (rec()) -> {[[rec()]],ets:continuation()} | '$end_of_table'

最新更新