Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5096040
  • 博文数量: 921
  • 博客积分: 16037
  • 博客等级: 上将
  • 技术积分: 8469
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-05 02:08
文章分类

全部博文(921)

文章存档

2020年(1)

2019年(3)

2018年(3)

2017年(6)

2016年(47)

2015年(72)

2014年(25)

2013年(72)

2012年(125)

2011年(182)

2010年(42)

2009年(14)

2008年(85)

2007年(89)

2006年(155)

分类: Erlang

2015-02-01 16:39:11

原文链接  

Erlang是一种“动态”语言,这会带来一个问题,单元测试不足以证明我写的代码是否足够正确。很难发现动态语言类型错用的问题。静态类型语言倒是很容易找到此类错误,但是Erlang是“动态的”。 

例如,length/1函数只能处理类型为列表(list)的参数,如果传入的不是列表,比如传入一个atom就会出错,但是程序中这样的代码是能够通过编译的,运气好的话会有一个警告,运气差的话只能在运行时发现出错。例如以下代码能成功的编译,也不会有警告,但是显然代码是有问题的,这个问题只能在运行时(foo函数被调用时)才能发现: 

  1. bar(List) ->
  2.     abc.

  3. foo() ->
  4.     V1 = bar([]),
  5.     io:format("length: ~p~n", [length(V1)]). %% 这里V1必须是list才行,但是编译时是没法知道的,只有运行时才会发现这个错误
同样的例子,exit/2函数的第一个参数必须是Pid,如果不是也能顺利通过编译,这样只能在运行时才会被发现 

此外,我自己写的函数,比如bar函数,可能业务逻辑决定了传入的参数必须是list,返回的参数也应该是list,如果不是,那调用者肯定错误的使用了此函数。在动态语言中很难做到对参数类型和返回类型的限制。 

也就是说,对库的接口的错误理解和错误使用是Erlang这样的动态语言常见问题。为此设计了一套合约语言(contract language),有了合约(contract),dialyzer能够很容易的检测到误用的接口 

有两种建立合约的方式,一种是在注释里使用@spec这样的annotation,另一种是spec声明 
例如规定bar函数只能接收atom或整数,只能返回atom的list: 

  1. -type bar_thing() :: atom() | integer(). %% 类型声明:定义bar函数能接收的参数类型
  2. -type ret_thing() :: [atom()]. %% 类型声明:定义bar函数的返回类型

  3. -spec bar(bar_thing()) -> ret_thing(). %% 函数合约:bar函数的参数和返回值

或者

  1. -spec bar(Arg::atom()|integer()) -> [atom()].
另一种是使用annotation的方式,(注意注释中spec要以点号结束annotation,不然无效): 

  1. %% @spec bar(Arg::atom()|integer()) -> [atom()].
正如注释一样,annotation不会影响编译。 

不过,违反了合约(contact)依然能顺利编译通过,但是我们现在就可以通过dialyzer工具分析源代码找出所有违反合约的代码 
dialyzer --src -c test1.erl 

可以一次分析工程中的所有文件 
dialyzer --src -I ./include -c *.erl 
但是可能会有太多的警告信息了,也可以一个文件一个文件的分析,处理起来容易一点 。

注1:使用dialyzer工具前需要先构建plt: 

  1. dialyzer --build_plt -r $OTP_HOME/lib/kernel-2.12.4/ebin\
  2. $OTP_HOME/lib/stdlib-1.15.4/ebin\
  3. $OTP_HOME/lib/mnesia-4.4.5/ebin (或者其它更多的模块)

这一过程耗时很长(大概5分多钟),成功后会在我的home目录下创建一个叫.dialyzer_plt的文件 
注2:有个万能类型any()可以代表任意的数据类型; 
注3:可以将多个文件中用到的类型(比如pos())集中到一个erl文件中(比如m.erl),通过m:pos()使用该类型;或者将该类型集中到头文件hrl中,使用时包含进来。 
注4:typer工具可以列出所有声明的合约 

总结: 
可以通过确定某种编程规范以及使用Dialyzer这样的工具分析代码是否正确,克服动态语言的弱点。声明和合约一般不影响编译和运行。所以编译通过不一定代表合约有效,还需要dialyzer工具分析 

更详细的介绍见 




阅读(1815) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~