如何用Erlang Dialyzer指定内部函数参数返回类型及变量类型
Erlang Dialyzer 内部函数类型约束与变量类型校验方案
一、约束库函数/Behavior函数的调用类型
不需要额外编写包装函数,有两种更直接的实现方式:
给当前函数添加
-spec标注:通过明确当前函数的输入输出类型,让Dialyzer自动推导并校验内部调用的函数参数。比如要确保maps:put/3的第三个参数是map(),可以给调用它的函数加类型标注:-spec update_map(Key :: term(), Value :: term(), Map :: map()) -> map(). update_map(Key, Value, Map) -> maps:put(Key, Value, Map).此时如果传入
maps:put的参数顺序错误(比如把Map放到第二个位置),Dialyzer会直接抛出类型不匹配的警告。针对Behavior函数(如
gen_server:reply/2),同样给回调函数加-spec即可。比如handle_call回调:-spec handle_call(Request :: term(), From :: gen_server:from(), State :: map()) -> {reply, Reply :: term(), NewState :: map()}. handle_call(_Request, From, State) -> gen_server:reply(From, ok), {reply, ok, State}.gen_server:from()是Behavior定义好的类型,Dialyzer会自动校验gen_server:reply的参数是否符合要求。使用类型断言或Guard约束:如果不想给整个函数加
-spec,可以在局部通过类型赋值或Guard明确变量类型:some_fun(Key, Value, Map) when is_map(Map) -> maps:put(Key, Value, Map).或者在函数内部直接标注变量类型:
some_fun() -> RawMap = #{a => 1}, Map :: map() = RawMap, % 明确Map为map类型 maps:put(b, 2, Map).
二、约束函数内/全局变量的类型
局部变量:直接在代码中通过类型赋值语句标注,Dialyzer会校验后续赋值是否符合类型:
calc() -> Count :: integer() = 0, % 若此处写Count = "string",Dialyzer会报错 Count + 1.如果变量是函数参数,也可以用Guard约束:
print_num(Num) when is_integer(Num) -> io:format("~p~n", [Num]).全局变量(模块属性):用
-assert_type/2标注模块属性的类型,确保全局值符合预期:-module(conf). -type port_num() :: integer(). -define(SERVER_PORT, 8080). -assert_type(?SERVER_PORT, port_num()). get_port() -> ?SERVER_PORT.若修改
?SERVER_PORT为非整数,Dialyzer会触发类型警告。
三、关于包装函数的方案
包装函数并非最优解,除非你需要对库函数做统一封装(比如添加默认逻辑、错误处理)。仅为类型校验的话,上述方法更直接,无需额外冗余代码,能让Dialyzer直接完成校验。
内容的提问来源于stack exchange,提问作者Piskator
相关产品推荐
相关产品推荐

