以下是一些带有错误的代码,我认为Dialyzer应该能够发现:
-module(myapp_thing).
-spec exists(pos_integer()) -> yes | no.
exists(Id) ->
myapp_mnesia:thing_exists(Id).
-module(myapp_mnesia).
thing_exists(Id) ->
Exists = fun() ->
case mnesia:read({thing, Id}) of
[] -> false;
_ -> true
end
end,
mnesia:activity(transaction, Exists).
myapp_thing:exists/1
被指定为返回yes | no
,但返回类型实际上是true | false
(即boolean()
),这是从myapp_mnesia:thing_exists/1
返回的。
然而,在myapp上运行Dialyzer会在没有警告的情况下通过它。
如果我将myapp_mnesia:thing_exists/1
更改为仅返回true
,我将得到适当的警告;类似地,如果我添加正确的规范:
-spec session_exists(pos_integer()) -> boolean().
但Dialyzer似乎无法查看mnesia事务函数Exists的内部,或者由于其他原因无法推断thing_Exists的返回类型。
那么,记忆事务函数是Dialyzer的一个障碍,还是Dialyzer返回类型推断有更普遍的障碍?
在mnesia_tm:execute_transaction
中,所提供的fun是在catch
内部调用的,这意味着就Dialyzer而言,返回类型会塌陷为term()
。因此Dialyzer无法得出mnesia:activity/2
的返回类型与所提供函数的返回类型相同的结论,因此需要一个显式的类型规范。
此外,我相信Dialyzer通常不会根据作为参数提供的函数的返回值来推断返回值类型。例如,使用此模块:
-module(foo).
-export([foo/1]).
foo(F) ->
F(42).
typer显示以下内容:
$ typer /tmp/foo.erl
%% File: "/tmp/foo.erl"
%% --------------------
-spec foo(fun((_) -> any())) -> any().
不过,如果我添加显式类型规范-spec foo(fun((_) -> X)) -> X.
,那么typer就会接受它
(我相信Dialyzer开发人员会对此有一个更完整、更有洞察力的答案。)