导出功能时,透明师不会检测到后卫违规

  • 本文关键字:功能 透明 erlang dialyzer
  • 更新时间 :
  • 英文 :


透析版2.9版。ERTS 7.3。OTP 18。

在以下人为的Erlang代码中:

-module(dialBug).
-export([test/0]).
%-export([f1/1]). % uncomment this line
test() ->
    f1(1).
f1(X) when X > 5 ->
    X*2.

当透明仪通过上述代码运行时,它警告说,该代码将不起作用,因为后卫测试(x> 5)永远无法成功。

但是,当我删除第三行并导出F1/1功能时,透明仪不再发出任何警告。

我意识到,当F1/1导出时,透明师不可能知道后卫条款将失败,因为外部客户可以使用它。但是,为什么它不再确定测试/0使用F1/1错误?

透明师作为类型检查器有一些限制。透明剂不是严格的,它是松散的 typer。这意味着,只有在发现函数的方式显然是错误的情况下,它才会给您警告,而不是与某种情况相反,因为它会导致呼叫者可能会做不好的事情。

它将尝试推断有关呼叫站点的事情,但它不能超越基本的Typespec声明可以传达的内容。因此,整数值可以定义为neg_integer()pos_integer()non_neg_integer()或任何integer(),但是除非您清楚地将边界定义为法律值,否则无法定义来自5..infinity的任意范围,但是您 can 定义诸如5..10之类的范围并获得您期望的结果。

奇怪的部分是,尽管警卫为透明师提供了一些信息,因为它是一个宽松/松散的typer,但真正的负担是在编码器上使用足够紧张的定义函数,以表明可以检测到呼叫站点的错误。<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

这是这些事物在实际代码 透析输出中的播放方式(带有我,这是一个完整显示所有这些内容的长屏幕,但没有什么比代码更好地证明相关问题了):

原始问题

-module(dial_bug1).
-export([test/0]).
%-export([f/1]).
test() ->
    f(1).
f(X) when X > 5 ->
    X * 2.

透析日:

dial_bug1.erl:5: Function test/0 has no local return
dial_bug1.erl:8: Function f/1 has no local return
dial_bug1.erl:8: Guard test X::1 > 5 can never succeed
 done in 0m1.42s
done (warnings were emitted)

因此,在一个封闭的世界中,我们可以看到透明师会回溯到呼叫者,因为它的情况有限。

第二个变体

-module(dial_bug2).
-export([test/0]).
-export([f/1]).
test() ->
    f(1).
f(X) when X > 5 ->
    X * 2.

透析说:

done (passed successfully)

在一个开放的世界中,呼叫者可以是任何人发送任何东西的人,没有努力回溯并检查未宣布的,无限的范围

第三变体

-module(dial_bug3).
-export([test/0]).
-export([f/1]).
-spec test() -> integer().
test() ->
    f(-1).

-spec f(X) -> Result
    when X      :: pos_integer(),
         Result :: pos_integer().
f(X) when X > 5 ->
    X * 2.

透析说:

dial_bug3.erl:7: Function test/0 has no local return
dial_bug3.erl:8: The call dial_bug3:f(-1) breaks the contract (X) -> Result when X :: pos_integer(), Result :: pos_integer()
 done in 0m1.28s
done (warnings were emitted)

在一个开放世界中我们将找到一个可宣布的开放范围(在这种情况下,是正整数的集合)。

第四变体

-module(dial_bug4).
-export([test/0]).
-export([f/1]).
-spec test() -> integer().
test() ->
    f(1).

-spec f(X) -> Result
    when X      :: pos_integer(),
         Result :: pos_integer().
f(X) when 5 =< X, X =< 10  ->
    X * 2.

透析说:

done (passed successfully)

在一个开放的世界中,我们有一个守卫,但仍未宣布的范围,我们发现透视将再次找不到违规呼叫者。在我看来,这是所有最重要的变体 - 因为我们知道dialyzer do 从守卫类型的守卫中获取提示,但显然它不是从中数字范围检查警卫。因此,让我们看看我们是否声明了有限的但任意的,范围...

第五变体

-module(dial_bug5).
-export([test/0]).
-export([f/1]).
-spec test() -> integer().
test() ->
    f(1).

-spec f(X) -> Result
    when X      :: 5..10,
         Result :: pos_integer().
f(X) when 5 =< X, X =< 10  ->
    X * 2.

透析说:

dial_bug5.erl:7: Function test/0 has no local return
dial_bug5.erl:8: The call dial_bug5:f(1) breaks the contract (X) -> Result when X :: 5..10, Result :: pos_integer()
 done in 0m1.42s
done (warnings were emitted)

,在这里,我们看到,如果我们勺子喂食透析仪,它将按预期完成工作。

我不确定这是"错误"还是"透明透光的限制"。疼痛的主要点透明地址为失败的本地类型,而不是数字界限。

所有的话...

当我在实际项目中有效的实际项目中遇到过这个问题时,我已经提前知道是否正在处理有效的数据't我总是将其写给一个:

  1. 彻底崩溃(从不返回不良结果!只是死了!这是我们的宗教信仰。)
  2. 返回表格{ok, Value} | {error, out_of_bounds}的包装值,然后让呼叫者决定该怎么做(在每种情况下,这为他们提供了更好的信息)。

一个受保护的示例是相关的 - 上面具有有限后卫的最后一个示例将是可崩溃的功能的正确版本。

-spec f(X) -> Result
    when X      :: 5..10,
         Result :: {ok, pos_integer()}
                 | {error, out_of_bounds}.
f(X) 5 =< X, X =< 10 ->
    Value = X * 2,
    {ok, Value};
f(_) ->
    {error, out_of_bounds}.

相关内容

  • 没有找到相关文章

最新更新