定义新的数据类型并给代码添加类型注解。
预定义数据类型
non_neg_integer() 非负的整数
pos_integer()是 一个正整数
neg_integer()是一个负整数
自定义类型
%% 格式
T1 :: A | B | C ...
%% 定义了一共int类型 表示整数
-type int() :: 0,1,-1,2,-2 ......
%% 类似枚举的用法
-type outType :: "DuiZhi","ShunZhi"...
内部定义类型
-spec file:open(FileName,Modes) -> {ok,Handle} | {error,Why} when
FileName :: string(),
Modes :: [Mode],
Mode :: read | write | ...
Handle :: file_handle(),
Why :: error_term().
b模块导入a模块的数据类型
-module(a).
-type rich_text() :: [{font(),char()}].
-type font() :: integer().
%% 开启其他模块导入权限
-export_type([rich_text/0,font/0]).
-module(b).
...
-spec rich_text_length(a:rich_text()) -> integer().
...
不透明类型opaque ,外部只能只有一个代表它的变量,而不应该知道其具体值。如果使用推到,会被dialyzer检测出来。
-module(a).
-opaque rich_text() :: [{font(),char()}].
-export_type([rich_text/0]).
%% 外部可调用这两个方法,中间用 rich_text的值连接
-export([make_text/1,bounding_box/1]).
-spec make_text(string()) -> rich_text().
-spec bounding_box(rich_text()) -> {Height::integer(),Width::integer()}.
-module(b).
...
do_this() ->
X = a:make_text("hello"), %% 调用第一个方法
{W,H} = a:bounding_box(X),
[F || {F,_}] <- X. %% 这一行是不正确的。不能知道内部具体值
dialyzer
使用dialyzer的最佳方式是将它用于每一个开发阶段。开始编写一个新模块时,应该首先考虑类型并声明它们,然后再编写代码。
特点:
- 避免使用-compile(export_all)。
- 为模块导出函数的所有参数提供详细的类型规范。尽量给导出函数的参数设置最严格的限制。
- 为记录定义里的所有元素提供默认的参数。
- 把匿名变量用作函数的参数经常会导致结果类型不如你预想得那么精确。
第一次运行dialyzer时,需要为打算使用的所有标准库类型建立缓存。
%% shell
dialyzer
%% 第一个命令会生成erts、stdlib和kernel的PLT
dialyzer --build_plt --apps erts kernel stdlib
内置函数返回值不 对应错误
-module(test1).
-export([f1/0]).
f1() ->
X = erlang:time(),
seconds(X).
seconds({_Year,_Month,_Day,Hour,Min,Sec}) ->
(Hour * 60 + Min)* 60 + Sec.
dialyzer test1.erl
下面异常说,erlang:time(). 的返回值只有3个 而不是6个。
传入参数异常:它告诉我们list_to_tuple期望的参数是[any()]类型的,而不是{'a','b','c'}。
添加约束条件
运算符号默认添加限定number
-module(types1).
-export([f1/1,f2/1,f3/1]).
f1({H,M,S}) -> (H+M*60)*60+S.
f2({H,M,S}) when is_integer(H) -> (H+M*60)*60+S.
f3({H,M,S}) when is_integer(H) ->
print(H,M,S),
(H+M*60)*60+S.
print(H,M,S) ->
Str = integer_to_list(H) ++ ":" ++ integer_to_list(M) ++ ":" ++ integer_to_list(S),
io:format("~s",[Str]).
查询f1(),f2(),f3() 的限定
typer types1.erl
%% 运算符默认限定number
-spec f1({number(),number(),number()}) -> number().
%% is_integer() 限定integer
-spec f2({integer(),number(),number()}) -> number().
-spec f3({integer(),integer(),integer()}) -> integer().
%% integer_to_list() 限定integer
-spec print(integer(),integer(),integer()) -> 'ok'.
然后就可以用dialyzer 查看是否书写错误(这个在java里idea直接会报红好么)。书写上和上面合不上就会抛异常