`
zhongwencool
  • 浏览: 27165 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

erlang的编程规范

阅读更多
Programming Rules and Conventions
erlang的编程规范

erlang的基本体系:
从文件构造,进程,进程间联系,函数来说清楚整个构架。

引用
Structure and Erlang Terminology

Erlang systems are divided into modules. Modules are composed of functions and attributes. Functions are either only visible inside a module or they are exported i.e. they can also be called by other functions in other modules. Attributes begin with "-" and are placed in the beginning of a module.

The work in a system designed using Erlang is done by processes. A process is a job which can use functions in many modules. Processes communicate with each other by sendingmessages. Processes receive messages which are sent to them, a process can decide which messages it is prepared to receive. Other messages are queued until the receiving process is prepared to receive them.

A process can supervise the existence of another process by setting up a link to it. When a process terminates, it automatically sends exit signals to the process to which it is linked. The default behavior of a process receiving an exit signal is to terminate and to propagate the signal to its linked processes. A process can change this default behavior by trapping exits, this causes all exit signals sent to a process to be turned into messages.

A pure function is a function that returns the same value given the same arguments regardless of the context of the call of the function. This is what we normally expect from a mathematical function. A function that is not pure is said to have side effects.

Side effects typically occur if a function a) sends a message b) receives a message c) calls exit d) calls any BIF which changes a processes environment or mode of operation (e.g. get/1, put/2, erase/1, process_flag/2 etc).


3.1 Export as few functions as possible from a module
对外部来说接口不变,但是可以改变它的实现方式,且整个module对使用者来说简单。

3.2 Try to reduce intermodule dependencies
每一次调用外部的函数都会在代码目录下遍历去找这个module,减少不必要的外部调用是最有效的方法。PS

Note also that it is desirable that the inter-module calling dependencies form a tree and not a cyclic graph.
不要出现A调用B,B调用C,C又调用A的情况。

3.3 Put commonly used code into libraries

The libraries should be collections of related functions,典范如lists module .The best library functions have no side effects. Libraries with functions with side effects limit the re-usability. 不应该有lists_maths module混合功能是一个非常不好的习惯!

3.4 Isolate "tricky" or "dirty" code into separate modules
把所有的脏操作,看上去不合理的操作都用单独的modules分离开:如以下情况:
使用进程字典;
用erlang:process_info/1,(这个真是奇怪的需求,原因是什么?)
其它你不想做,但是又不得不做的情况。
对于这种代码一定要写好comment!

3.5 Don't make assumptions about what the caller will do with the results of a function
不要去假定使用者会在对的情况下怎样,错的情况下怎样。要让他们自己处理。比如:
do_something(Args) ->
  case check_args(Args) of
    ok ->
      {ok, do_it(Args)};
    {error, What} ->
      String = format_the_error(What),
      io:format("* error:~s\n", [String]), %% Don't do this
      error
  end.

Instead write something like:

do_something(Args) ->
  case check_args(Args) of
    ok ->
      {ok, do_it(Args)};
    {error, What} ->
      {error, What}
  end.

error_report({error, What}) ->
  format_the_error(What).

上面的写法把对错误的处理规定死在终端上打印一下,而返回一个不知原因的error,使下面的程序不知原因就终止了。这个不好。为了让接下来的程序根据错误的情况来选择执行方式,就要返回reason啊!

3.6 Abstract out common patterns of code or behavior
Avoid "copy" and "paste" programming, use functions! 如果两个函数有共同的特点,就把共同的部分抽象出来。

3.7 Top-down
要使用从高层到低层的写法,要先明白这个函数是搞什么的,然后把结构定好,再去回头写那些细节。不要想一口气从头写完函数。

3.8 Don't optimize(优化) code
Don't optimize your code at the first stage. First make it right, then (if necessary) make it fast (while keeping it right).先做对,再做好。
3.9 Use the principle of "least astonishment"
不要写出让人惊讶的函数,模块,不然就是你的名字起错了!!!看上去整体风格都是一样的!
If you get astonished by what a function does, either your function solves the wrong problem or it has a wrong name.

3.10 Try to eliminate side effects.排除边界效应。
边界效应都是不可逆的,如改变环境变量,发消息 什么的。如果要写这样的函数,一定要独立且加注释啊!
Collect together the functions which have side effect and clearly document all the side effects.

3.11 Don't allow private data structure to "leak" out of a module
时刻想着如果要重写这个模块(里面的数据结构变化了,便是要求外部的调用不用变!
-module(queue).
-export([add/2, fetch/1]).

add(Item, Q) ->
  lists:append(Q, [Item]).

fetch([H|T]) ->
  {ok, H, T};
fetch([]) ->
  empty.

Better is:

-module(queue).
-export([new/0, add/2, fetch/1]).

new() ->
  [].

add(Item, Q) ->
  lists:append(Q, [Item]).

fetch([H|T]) ->
  {ok, H, T};
fetch([]) ->
  empty.

Now we can write:

NewQ = queue:new(),
Queue1 = queue:add(joe, NewQ),
Queue2 = queue:add(mike, Queue1), ...

Len = length(Queue) % Don't do this

since they know that the queue is represented as a list. Again this is bad programming practice and leads to code which is very difficult to maintain and understand. If they need to know the length of the queue then a length function must be added to the module, thus:

-module(queue).
-export([new/0, add/2, fetch/1, len/1]).

new() -> [].

add(Item, Q) ->
  lists:append(Q, [Item]).

fetch([H|T]) ->
  {ok, H, T};

fetch([]) ->
  empty.

len(Q) ->
  length(Q).

-module(queue).
-export([new/0, add/2, fetch/1, len/1]).

new() ->
  {[],[]}.

add(Item, {X,Y}) -> % Faster addition of elements
  {[Item|X], Y}.

fetch({X, [H|T]}) ->
  {ok, H, {X,T}};

fetch({[], []) ->
  empty;

fetch({X, []) ->
  % Perform this heavy computation only sometimes.
  fetch({[],lists:reverse(X)}).

len({X,Y}) ->
  length(X) + length(Y).

3.12 Make code as deterministic(确定)as possible.
使函数运行多少次的结果都一致,如开启5个进程:一下开启5个后检查是不是开启了,更好的做法是每开启一个看看是不是以前的都开启了。

3.13 Do not program "defensively"
不要防护性编程,在你不使用catch的情况下,让程序在unexpect下crash吧。这就是最好的实践了。

3.14 Isolate hardware interfaces with a device driver
硬件的操作要接口封装好,数据库操作要封装!

3.15 Do and undo things in the same function
分清好层次关系,如打开文件-》开了后操作,关闭文件   ;->没开error  end 这里的关闭文件 和开启文件是同一个级别的,不要把这封装到下一级(子函数)里面去了。
do_something_with(File) ->
  case file:open(File, read) of,
    {ok, Stream} ->
      doit(Stream),
      file:close(Stream) % The correct solution
    Error -> Error
  end.

4 Error Handling
4.1 Separate error handling and normal case code(与防护性编程一样的原则)
Don't clutter code for the "normal case" with code designed to handle exceptions. As far as possible you should only program the normal case. If the code for the normal case fails, your process should report the error and crash as soon as possible. Don't try to fix up the error and continue. The error should be handled in a different process (See "Each process should only have one "role"" on page 15.).

Clean separation of error recovery code and normal case code should greatly simplify the overall system design.

The error logs which are generated when a software or hardware error is detected will be used at a later stage to diagnose and correct the error. A permanent record of any information that will be helpful in this process should be kept.

4.2 Identify the error kernel(分清哪一些是可以出错的,哪一些核心是不可出错的?)
One of the basic elements of system design is identifying which part of the system has to be correct and which part of the system does not have to be correct.

In conventional operating system design the kernel of the system is assumed to be, and must be, correct, whereas all user application programs do not necessarily have to be correct. If a user application program fails this will only concern the application where the failure occurred but should not affect the integrity of the system as a whole.

The first part of the system design must be to identify that part of the system which must be correct; we call this the error kernel. Often the error kernel has some kind of real-time memory resident data base which stores the state of the hardware.

5 Processes, Servers and Messages

5.1 Implement a process in one module
The code for the top loop of a process should not be split into several modules - this would make the flow of control extremely difficult to understand. This does not mean that one should not make use of generic server libraries, these are for helping structuring the control flow.

5.2 Use processes for structuring the system
Processes are the basic system structuring elements. But don't use processes and message passing when a function call can be used instead.
能用函数代码发消息的就不要用发消息+进程来计算东西了。

5.3 Registered processes
进程要注册!才能好理解。

5.4 Assign exactly one parallel process to each true concurrent activity in the system
"Use one parallel process to model each truly concurrent activity in the real world"
决定用并行的进程来描述需求的准则是,使代码看上去和真实世界一样吧。
If there is a one-to-one mapping between the number of parallel processes and the number of truly parallel activities in the real world, the program will be easy to understand.

5.5 Each process should only have one "role"
每个进程只负责自己的责任,不用管别人的。
Supervisor: watches other processes and restarts them if they fail.
Worker: a normal work process (can have errors).
Trusted Worker: not allowed to have errors.

5.6 Use generic functions for servers and protocol handlers wherever possible
尽量使用系统自带的OTP gen_server gen_fsm。。。。

5.7 Tag messages
发消息时一定要在头标记一个atom的tuple来标记消息 ,PID !{for_fun,Test,1,2}.

5.8 Flush unknown messages
main_loop() ->
  receive
    {msg1, Msg1} ->
      ...,
      main_loop();
    {msg2, Msg2} ->
      ...,
      main_loop();
    Other -> % Flushes the message queue.
      error_logger:error_msg(
          "Error: Process ~w got unknown msg ~w~n.",
          [self(), Other]),
      main_loop()
  end.
不要让不明消息一直在邮箱里面啊。多了就不会太长。

5.9 Write tail-recursive servers
loop() ->
  receive
    {msg1, Msg1} ->
      ...,
      loop();
    stop ->
      true;
    Other ->
      error_logger:log({error, {process_got_other, self(), Other}}),
      loop()
  end,
  io:format("Server going down").                % Don't do this!
                % This is NOT tail-recursive
5.10 Interface functions
不要直接发消息,都封装好。

5.11 Time-outs
Be careful when using after in receive statements. Make sure that you handle the case when the message arrives later (See "Flush unknown messages" on5.8.).

5.12 Trapping exits
As few processes as possible should trap exit signals. Processes should either trap exits or they should not. It is usually very bad practice for a process to "toggle"(栓牢套住) trapping exits.

6 Various Erlang Specific Conventions
6.1 Use records as the principle data structure
The record features of Erlang can be used to ensure cross module consistency of data structures and should therefore be used by interface functions when passing data structures between modules.

6.2 Use selectors and constructors
选择器和构造器。
demo() ->
  P = #person{name = "Joe", age = 29},
  #person{name = Name1} = P,% Use matching, or...
  Name2 = P#person.name. % use the selector like this.

6.3 Use tagged return values
just like 5.7 Tag message

6.4 Use catch and throw with extreme care
不要用catch throw
Catch and throw can be useful when the program handles complicated and unreliable input (from the outside world, not from your own reliable program) that may cause errors in many places deeply within the code. One example is a compiler.

6.5 Use the process dictionary with extreme care
不要用进程字典:The use of get and put will cause a function to behave differently when called with the same input at different occasions. This makes the code hard to read since it is non-deterministic.

6.6 Don't use import
  Hard to understand.

6.7 Exporting functions
It is a user interface to the module.
It is an interface function for other modules.
It is called from apply, spawn etc. but only from within its module.

7 Specific Lexical and Stylistic Conventions
7.1 Don't write deeply nested code嵌套要少一点。

7.2 Don't write very large modules
400 lines是极限

7.3 Don't write very long functions
Don't write functions with more than 15 to 20 lines of code.

7.4 Don't write very long lines
Don't write very long lines. A line should not have more than 80 characters.

7.5 Variable names
Choose meaningful variable names - this is very difficult.

7.6 Function names
The function name must agree exactly with what the function does.

7.7 Module names
Erlang has a flat module structure (i.e. there are not modules within modules).

7.8 Format programs in a consistent manner
统一风格 {1,2,3}    VS { 1 , 2 , 3 }


8 Documenting Code
8.1 Attribute code
You must always correctly attribute all code in the module header. Say where all ideas contributing to the module came from - if your code was derived from some other code say where you got this code from and who wrote it.
Never steal code - stealing code is taking code from some other module editing it and forgetting to say who wrote the original.

Examples of useful attributes are:
-revision('Revision: 1.14 ').
-created('Date: 1995/01/01 11:21:11 ').
-created_by('eklas@erlang').
-modified('Date: 1995/01/05 13:04:07 ').
-modified_by('mbj@erlang').


8.2 Provide references in the code to the specifications
8.3 Document all the errors
8.4 Document all the principle data structures in messages
8.5 Comments
     Make sure that comments are kept up to date with the code.

8.6 Comment each function return 一定要写清楚

8.7 Data structures
The record should be defined together with a plan text description. Example:

%% File: my_data_structures.h

%%---------------------------------------------------------------------
%% Data Type: person
%% where:
%%    name: A string (default is undefined).
%%    age: An integer (default is undefined).
%%    phone: A list of integers (default is []).
%%    dict:     A dictionary containing various information about the person. 
%%       A {Key, Value} list (default is the empty list).
%%----------------------------------------------------------------------

8.11 Do not comment out old code - remove it
8.12 Use a source code control system
     All non trivial projects must use a source code control system such as RCS, CVS or Clearcase to keep track of all modules.

9. the most common mistakes:
  • 一个函数写太多页了
  • 嵌套写太深了
  • 函数返回值不标记
  • 函数名与函数功能不对应
  • 变量名没有意义
  • 在不该用进程的地方用开启进程
  • 数据结构错
  • 不注释或乱注释
  • 使用进程字典
  • 对邮箱里的消息不flush time_outs处理


英文地址:http://www.erlang.se/doc/programming_rules.shtml#REF47531




  • 大小: 15.5 KB
分享到:
评论

相关推荐

    erlang 编程规范

    很有用的写erlang程序的程序规范。 1.1 Program Development Using Erlang - Programming Rules and Conventions This paper lists some aspects which should be taken into consideration when specifying and ...

    Erlang编程指南

    “即便我已经使用Erlang多年,在编程的时候仍然需要参考《Erlang编程指南》。不同层次的Erlang程序员都会发现本书是有价值的学习和参考资料。”, ——Steve Vinoski,《IEEE Internet Computing》专栏作家, 《Erlang...

    rabbitMQ的api及规范说明书.doc

    Mq 一种应用程序对应用程序的通信方法. AMQP 一个提供统一消息服务的应用层标准高级消息队列协议 Erlang Erlang是一种通用的面向并发的编程语言,目的是创造一种可以应对大规模并发活动的编程语言和运行环境。

    毕业设计论文范文源码-slg-server:erlang游戏服务器框架

    毕业设计论文范文源码 0. 获取代码 首先建立你自己的git仓库,然后执行git ...slg_csv:游戏中常常有gd配置文件,slg_csv提供配置文件到ets表的直接映射,便于编程访问. slg_model:本框架使用ets在内存中cache玩家

    Workflow Execution Engine:高性能的工作流程,对并行任务的强大支持-开源

    工作流执行引擎(WEE)是奥地利维也纳大学创建的一种领域特定语言。 该语言的主要目标是为现有工作流程规范提供一种简单,轻便且... 该项目包含使用Erlang编程语言对该语言的实现。 请参阅项目Wiki了解更多详细信息。

    Windows下的文本编辑器-flexedit

    FlexEdit Windows下的文本/十六进制编辑器,为编程人员提供方便强大的文本/十六进制编辑器。FlexEdit为公益软件,免费使用,没有任何功能限制。 基于Scitilla的强大的编辑功能 能够高亮显示的语言列表有:ada, asm,...

    消息队列RabbitMQ入门与5种模式详解

    RabbitMQ是开源的,实现了AMQP协议的,采用Erlang(面向并发编程语言)编写的,可复用的企业级消息系统;AMQP(高级消息队列协议)是一个异步消息传递所使用应用层协议规范,为面向消息中间件设计,基于此协议的客户端...

    lucazulian:我的“特殊”存储库

    我想在Haskell,Rust,Erlang,OCaml,Scheme上做更多的工作。 我相信开源软件的价值。 我目前的兴趣集中在功能编程,算法,编译器,解释器,特定领域的语言,虚拟机和自主机器人上。 :raising_hands: 我(当前...

    nucleus:一个基于 Akka IO 的轻量级React式 RPC 类系统

    它的协议很容易在其他编程语言中实现,并且松散地建模为规范。 它使用 Erlang 的来序列化和反序列化要通过线路发送的内容。 内部工作被构建为尽可能类型安全,而外部 API 使其易于在其他(动态)语言中使用。 作为...

Global site tag (gtag.js) - Google Analytics