IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Erlang类型及函数声明规格

    DLive发表于 2014-12-11 06:42:34
    love 0


    Author: Mail: Date: Copyright:

    litaocheng
    litaocheng@gmail.com
    2009.6.8
    This document has been placed in the public domain.

    Contents

    • 概述
    • 意义
    • 规范
      • 类型及其定义语法
      • 自定义类型定义
      • 在record中使用类型声明
      • 函数规范定义
    • 使用dialyzer进行静态分析
      • 生成plt
      • 使用dialyzer分析
    • 参考

    概述

    Erlang为动态语言,变量在运行时动态绑定,这对于我们获取函数的参数及返回值的类型信息具有一定的难度。 为了弥补这个不足,在Erlang中我们可以通过type及spec定义数据类型及函数原型。通过这些信息,我们对函数及调用进行静态检测, 从而发现一些代码中问题。同时,这些信息也便于他人了解函数接口,也可以用来生成文档。

    意义

    • 定义各种自定义数据类型
    • 定义函数的参数及返回值
    • dialyzer 进行代码静态分析
    • edoc利用这些信息生成文档

    规范

    类型及其定义语法

    数据类型由一系列Erlang terms组成,其有各种基本数据类型组成(如 integer() , atom() , pid() )。Erlang预定义数据类型代表属于此类型的所有数据,比如 atom() 代表所有的atom类型的数据。

    数据类型,由基本数据类型及其他自定义数据类型组成,其范围为对应数据类型的合集。 比如:

    atom() | 'bar' | integer() | 42
    

    与:

    atom() | integer()
    

    具有相同的含义。

    各种类型之间具有一定的层级关系,其中最顶层的 any() 可以代表任何Erlang类型, 而最底层的 none() 表示空的数据类型。

    预定义的类型及语法如下:

    Type :: any()           %% 最顶层类型,表示任意的Erlang term
         | none()           %% 最底层类型,不包含任何term
         | pid()
         | port()
         | ref()
         | []               %% nil
         | Atom
         | Binary
         | float()
         | Fun
         | Integer
         | List
         | Tuple
         | Union
         | UserDefined      %% described in Section 2
    
    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()], 而 [] ,仅仅 表示一个单独的类型即空列表。

    为了方便,下面是一个内建类型列表

    Built-in type Stands for

    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()

    类型定义不可重名,编译器可以进行检测。

    注意 : 还存在一些其他 lists 相关的内建类型,但是因为其名字较长,我们很少使用:

    nonempty_maybe_improper_list(Type) :: nonempty_maybe_improper_list(Type, any())
    nonempty_maybe_improper_list() :: nonempty_maybe_improper_list(any())
    

    我们也可以使用record标记法来表示数据类型:

    Record :: #Erlang_Atom{}
            | #Erlang_Atom{Fields}
    

    当前R13B中,已经支持record定义中的类型说明

    自定义类型定义

    通过前一章节的介绍,我们知道基本的类型语法为一个atom紧随一对圆括号。如果我们想 第一个一个新类型,需要使用 ‘type’ 关键字:

    -type my_type() :: Type.
    

    my_type为我们自定义的type名称,其必须为atom,Type为先前章节介绍的各种类型, 其可以为内建类型定义,也可以为可见的(已经定义的)自定义数据类型。否则会 编译时保错。

    这样递归的类型定义,当前还不支持。

    类型定义也可以参数化,我们可以在括号中包含类型,如同Erlang中变量定义, 这个参数必须以大写字母开头,一个简单的例子:

    -type orddict(Key, Val) :: [{Key, Val}].
    

    在record中使用类型声明

    我们可以指定record中字段的类型,语法如下:

    -record(rec, {field1 :: Type1, field2, field3 :: Type3}).
    

    如果字段没有指明类型声明,那么默认为 any() . 比如,上面的record定义与此相同:

    -record(rec, {field1 :: Type1, field2 :: any(), field3 :: Type3}).
    

    如果我们在定义record的时候,指明了初始值,类型声明必须位于初始值之后:

    -record(rec, {field1 = [] :: Type1, field2, field3 = 42 :: Type3})$
    我们可以指定record中字段的类型,语法如下::
    
     -record(rec, {field1 :: Type1, field2, field3 :: Type3}).
    

    如果字段没有指明类型声明,那么默认为 any() . 比如,上面的record定义与此相同:

    -record(rec, {field1 :: Type1, field2 :: any(), field3 :: Type3}).
    

    如果我们在定义record的时候,指明了初始值,类型声明必须位于初始值之后:

    -record(rec, {field1 = [] :: Type1, field2, field3 = 42 :: Type3}).
    

    如果初始值类型与字段的类型声明不一致,会产生一个编译期错误。 filed的默认值为 ‘undefined’ ,因此下面的来个record定义效果相同:

    -record(rec, {f1 = 42 :: integer(),
                    f2      :: float(),
                    f3      :: 'a' | 'b').
    
    -record(rec, {f1 = 42 :: integer(),
                    f2      :: 'undefined' | float(),
                    f3      :: 'undefined' | 'a' | 'b').
    

    所以,推荐您在定义record时,指明初始值。

    recordion 'spec':fa/1. The success typing is (non_neg_integer()) -> pos_integer() spec.erl:22: Function some_fun/0 has no local return spec.erl:24: The call lists:keydelete(1,'foo',L::[{'bar',23} | {'foo',33},...]) will never return since it differs in argument position 2 from the success typing arguments: (any(),pos_integer(),maybe_improper_list()) done in 0m0.29s done (warnings were emitted)

    我们可以看到,我们的fa/1函数的spec信息错误,我们进行修正:

    由
    -spec fa( non_neg_integer() ) -> pos_integer().
    改为:
    -spec fa( N :: atom() ) -> pos_integer().
    

    some_fun中,lists:keydelete/3参数顺序进行修改:

    lists:keydelete(1, foo, L).
    改为:
    lists:keydelete(foo,1, L).
    

    重新编译,进行dialyzer分析,提示成功:

    litao@litao:~/erltest$ dialyzer -r ./spec
    Checking whether the PLT /home/litao/.dialyzer_plt is up-to-date... yes
    Proceeding with analysis... done in 0m0.28s
    done (passed successfully)
    
    转自:http://erlangdisplay.iteye.com/blog/404570
    


沪ICP备19023445号-2号
友情链接