透析版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我总是将其写给一个:
- 彻底崩溃(从不返回不良结果!只是死了!这是我们的宗教信仰。)
- 返回表格
{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}.