通过这些信息,我们对函数及调用进行静态检测,从而发现一些代码中问题。
同时,这些信息也便于他人了解函数接口,也可以用来生成文档。
Type :: any() %% 最顶层类型,表示任意的Erlang term
| none() %% 最底层类型,不包含任何term
| pid() | port() | ref() | [] | Atom | Binary | float()
| Fun | Integer | List | Tuple | Union | UserDefined
Union :: Type1 | Type2
Atom :: atom()
| Erlang_Atom %% 'foo', 'bar', ...
Binary :: binary() %% <<_:_ * 8>>
| <<>>
| <<_:Erlang_Integer>> %% Base size
| <<_:_*Erlang_Integer>> %% Unit size
| <<_:Erlang_Integer, _:_*Erlang_Integer>>
Fun :: fun() %% 任意函数
| fun((...) -> Type) %% 任意arity, 只定义返回类型
| fun(() -> Type)
| fun((TList) -> Type)
Integer :: integer()
| Erlang_Integer %% ..., -1, 0, 1, ... 42 ...
| Erlang_Integer..Erlang_Integer %% 定义一个整数区间
List :: list(Type) %% 格式规范的list (以[]结尾)
| improper_list(Type1, Type2) %% Type1=contents, Type2=termination
| maybe_improper_list(Type1, Type2) %% Type1 and Type2 as above
Tuple :: tuple() %% 表示包含任意元素的tuple
| {}
| {TList}
TList :: Type
| Type, TList
由于 lists 经常使用,我们可以将 list(T) 简写为 [T] ,而 [T, ...] 表示一个非空的元素类型为T的规范列表。两者的区别是 [T] 可能为空,而 [T, ...] 至少包含一个元素。
'_' 可以用来表示任意类型。
list()表示任意类型的list,其等同于 [_]或[any()], 而 [] ,仅仅 表示一个单独的类型即空列表。
为了方便,下面是一个内建类型列表
term() any()
bool() 'false' | 'true'
byte() 0..255
char() 0..16#10ffff
non_neg_integer() 0..
pos_integer() 1..
neg_integer() ..-1
number() integer() | float()
list() [any()]
maybe_improper_list() maybe_improper_list(any(), any())
maybe_improper_list(T) maybe_improper_list(T, any())
string() [char()]
nonempty_string() [char(),...]
iolist()
maybe_improper_list(char() | binary() | iolist(), binary() | [])
module() atom()
mfa() {atom(),atom(),byte()}
node() atom()
timeout() 'infinity' | non_neg_integer()
no_return() none()
定义新的类型:
-type NewTypeName(TVar1, TVar2, ... TVarN) :: Type.
函数规范的编写方式
-spec functionName(T1, T2, ..., Tn) -> Tret when
Ti :: Typei,
Tj :: Typej,
...
导出类型:
-export_type(typeName/0, typeName2/0).
不透明类型:
-opaque XXX : YYY.
隐藏XXX的内部细节。理论上是不希望在模块外部使用XXX的内部细节。
dialyzer
检测程序里的类型错误。主要是针对类型错误。
$dialyzer test.erl
dialyzer很容易受到干扰。所以要遵循几条简单规则:
避免使用-compile(export_all)。
为模块导出函数的所有函数提供详细的类型规范。
为记录定义里的所有元素提供默认的参数。
把匿名变量作用函数的参数经常会导致结果类型不如预想的那么精确。
总结:这是一个很有意思的功能,比C++的强制类型更加宽松,比Lua的弱类型又多了一个选择。算是一个亮点。