Erlang 将“any()”转换为“any()”是让透析器接受ETS匹配模式的好方法吗?

Erlang 将“any()”转换为“any()”是让透析器接受ETS匹配模式的好方法吗?,erlang,dialyzer,Erlang,Dialyzer,铸造到any()是让透析器接受ETS匹配模式的好方法吗 透析器和火柴规格不能很好地结合在一起,而且似乎没有一个标准的解决方案: 下面是我正在考虑的解决方案的完整示例。如果最后一行的matcher(“')更改为“”,则透析器会抱怨记录结构不好,但使用matcher/1功能似乎一切正常: -模块(示例)。 -记录(rec,{field::number})。 -导出([main/1])。 -键入matchvar():“$1'|”$2'|“$3'|”$4'|“$5'|”$6'|“$7'|”$8

铸造到
any()
是让透析器接受ETS匹配模式的好方法吗

透析器和火柴规格不能很好地结合在一起,而且似乎没有一个标准的解决方案:

下面是我正在考虑的解决方案的完整示例。如果最后一行的
matcher(“')
更改为“”,则透析器会抱怨记录结构不好,但使用
matcher/1
功能似乎一切正常:


-模块(示例)。
-记录(rec,{field::number})。
-导出([main/1])。
-键入matchvar():“$1'|”$2'|“$3'|”$4'|“$5'|”$6'|“$7'|”$8'|“$9'|”$10'|“$11'|”$12'|“$13'|”$14'|“$15'|”$17'|“$18'|“$19'|”$20'|$22”。
-规范匹配器(“|”matchvar())->any()。
匹配器(X)->
的案例节点()
“$将永远不会匹配”->二进制_to_term();
_->X
结束。
main(_Args)->
ets:match('my_table',#rec{field=matcher('$1')},1)。
这是因为透析器不能静态地判断
matcher/1
的unreachable第一句是unreachable。由于
binary\u to\u term/1
返回
any()
,透析器将
matcher/1
的返回类型推断为
any()

当使用匹配规格时,这个技巧是保持透析器愉快的好方法吗?“好”是指:

  • 低运行成本
  • 很少有步兵
  • 没有更好(更安全、更快、更符合人体工程学)的方法
我瞥了一眼
node()
的实现,认为它只是一个指针解引用,所以成本应该很低。而“$永远不会匹配”实际上永远不会匹配,因为
node()
。但一定有更好的办法

这里确实有两个问题,我将它们结合起来以避免:

  • 上述技术是否是让透析器将某物视为
    any()
    的好方法
  • 让透析器将匹配器(“UU”)视为
    any()
    处理匹配规格的好方法吗

  • 我认为这不是一个好的解决方案,因为您在编译时做了无用的工作(无论多么小)来满足某些要求,并且这样做是在欺骗透析器

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

    您始终可以仅导出实际类型的子类型,而不使用
    -opaque
    (详细说明):

    -模块(示例用户)。
    -导出([main\u ok/0,main\u error/0])。
    main_ok()->
    示例:main({rec,1})。
    主_错误()->
    示例:main({rec,'.'})。
    
    sample_user.erl
    7:函数main\u error/0没有本地返回
    8:调用示例:main({'rec','.})打破契约(rec())->{[[rec()]],ets:continuation()}|'$end_of_table'
    
    Re“在编译时做无用的工作(无论多么小)以满足某些要求”-你知道有没有人建议为透析器进行零成本转换?@MaxHeiber我不确定我是否理解这个问题:透析器在类型上工作,所以运行时的成本是0。也许你的意思是类似于
    -compile({inline,[build\u match/1]})-规范构建匹配(rec())->#rec{}。build_match(Rec)->Rec#Rec{field=''''}?我忘了内联的
    了,太好了!我在想Flow的类型转换断言()mypy的类型转换或TypeScript
    作为
    类型断言:。@MaxHeiber我在Dialyzer中没有看到这一点,我认为,为了能够自由地投射任何类型,您需要像在示例中那样抑制警告或欺骗透析器。我试图推荐在大型代码库中使用透析器的最佳实践,不幸的是,我不认为开发人员会支持此解决方案。要维护的代码太多了,特别是对于大型记录:类型别名必须复制每个字段的信息。
    -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).