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

erlang的模块参数化及其实现

阅读更多
Parameterized modules in Erlang
请参考这篇文章 http://ftp.sunet.se/pub/lang/erlang/workshop/2003/paper/p29-carlsson.pdf

我这里讲述的重点是如何实现的。先看代码:

root@nd-desktop:~/test/m# cat main.erl
% File: main.erl
-module(main).
-export([start/0]).
start() ->
M1 = print:new("Humpty"),
M2 = print:new("Dumpty"),
M1:message("Hello!"),
M2:message("Hi!"),
ok.
root@nd-desktop:~/test/m# cat print.erl
% File: print.erl
-module(print, [Name]).
-export([message/1]).

message(Text) ->
io:fwrite("~s: '~s'~n", [Name, Text]),
ok.

编译运行
root@nd-desktop:~/test/m# erlc *.erl
root@nd-desktop:~/test/m# erl -noshell -s main -s erlang halt
Humpty: 'Hello!'
Dumpty: 'Hi!'

在使用上是模拟面向对象的, 面向对象的同学会感到很亲切。但是erlang如何做到这个魔术的呢?

我们来分析下:

root@nd-desktop:~/test/m# erlc +"'S'" *.erl

root@nd-desktop:~/test/m# cat main.S
{module, main}.  %% version = 0

{exports, [{module_info,0},{module_info,1},{start,0}]}.

{attributes, []}.

{labels, 7}.


{function, start, 0, 2}.
  {label,1}.
    {func_info,{atom,main},{atom,start},0}.
  {label,2}.
    {allocate_zero,1,0}.
    {move,{literal,"Humpty"},{x,0}}.
    {call_ext,1,{extfunc,print,new,1}}.
    {move,{x,0},{y,0}}.
    {move,{literal,"Dumpty"},{x,0}}.
    {call_ext,1,{extfunc,print,new,1}}.
    {move,{x,0},{x,3}}.
    {move,{y,0},{x,1}}.
    {move,{atom,message},{x,2}}.
    {move,{literal,"Hello!"},{x,0}}.
    {move,{x,3},{y,0}}.
    {apply,1}.
    {move,{y,0},{x,1}}.
    {move,{atom,message},{x,2}}.
    {trim,1,0}.
    {move,{literal,"Hi!"},{x,0}}.
    {apply,1}.
    {move,{atom,ok},{x,0}}.
    {deallocate,0}.
    return.


{function, module_info, 0, 4}.
  {label,3}.
    {func_info,{atom,main},{atom,module_info},0}.
  {label,4}.
    {move,{atom,main},{x,0}}.
    {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.


{function, module_info, 1, 6}.
  {label,5}.
    {func_info,{atom,main},{atom,module_info},1}.
  {label,6}.
    {move,{x,0},{x,1}}.
    {move,{atom,main},{x,0}}.
    {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.

root@nd-desktop:~/test/m# cat print.S
{module, print}.  %% version = 0

{exports, [{instance,1},{message,2},{module_info,0},{module_info,1},{new,1}]}.

{attributes, [{abstract,[true]}]}.

{labels, 11}.


{function, new, 1, 2}.
  {label,1}.
    {func_info,{atom,print},{atom,new},1}.
  {label,2}.
    {call_only,1,{f,4}}.


{function, instance, 1, 4}.
  {label,3}.
    {func_info,{atom,print},{atom,instance},1}.
  {label,4}.
    {test_heap,3,1}.
    {put_tuple,2,{x,1}}.
    {put,{atom,print}}.
    {put,{x,0}}.
    {move,{x,1},{x,0}}.
    return.


{function, message, 2, 6}.
  {label,5}.
    {func_info,{atom,print},{atom,message},2}.
  {label,6}.
    {test,is_tuple,{f,5},[{x,1}]}.
    {test,test_arity,{f,5},[{x,1},2]}.
    {allocate_heap,0,4,2}.
    {get_tuple_element,{x,1},1,{x,2}}.
    {put_list,{x,0},nil,{x,0}}.
    {put_list,{x,2},{x,0},{x,1}}.
    {move,{literal,"~s: '~s'~n"},{x,0}}.
    {call_ext,2,{extfunc,io,fwrite,2}}.
    {move,{atom,ok},{x,0}}.
    {deallocate,0}.
    return.


{function, module_info, 0, 8}.
  {label,7}.
    {func_info,{atom,print},{atom,module_info},0}.
  {label,8}.
    {move,{atom,print},{x,0}}.
    {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.


{function, module_info, 1, 10}.
  {label,9}.
    {func_info,{atom,print},{atom,module_info},1}.
  {label,10}.
    {move,{x,0},{x,1}}.
    {move,{atom,print},{x,0}}.
    {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.

先看
print.S
这个模块导出的函数除了module_info外还有3个导出 new/1, instance/1, 更重要的是 message在源码里面是/1, 但是导出是/2,很奇怪是吗?

我们再看下 new 和 instance 返回一个tuple{print, Arg}.
message函数呢?
    {test,is_tuple,{f,5},[{x,1}]}.   %% 判断第二个参数是否是tuple
    {test,test_arity,{f,5},[{x,1},2]}. %%tuple的个数是不是2
    {allocate_heap,0,4,2}. 
    {get_tuple_element,{x,1},1,{x,2}}. %%取出tuple的第二个参数, 这个就是源码里面的Name模块参数
    {put_list,{x,0},nil,{x,0}}.
    {put_list,{x,2},{x,0},{x,1}}.

我演示下:
root@nd-desktop:~/test/m# erl
Erlang R13B02 (erts-5.7.3) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.3  (abort with ^G)
1> m(print).
Module print compiled: Date: September 25 2009, Time: 07.23
Compiler options:  [{cwd,"/root/test/m"},{outdir,"/root/test/m"}]
Object file: /root/test/m/print.beam
Exports:
         instance/1
         message/2
         module_info/0
         module_info/1
         new/1
ok
2> print:new(tag1).
{print,tag1}
3> print:instance(tag2).
{print,tag2}
4> print:message("hello", print:new(tag1)).
tag1: 'hello'
ok

main.S 就是这么调用我们的print模块的。
    {move,{literal,"Dumpty"},{x,0}}.
    {call_ext,1,{extfunc,print,new,1}}.
    {move,{x,0},{x,3}}.
    {move,{y,0},{x,1}}.
    {move,{atom,message},{x,2}}.
    {move,{literal,"Hello!"},{x,0}}.
    {move,{x,3},{y,0}}.
    {apply,1}.

压入的参数正是 (Message, print:new(tag)), 然后是apply opcode.

我们看下apply opcode:

OpCase(i_apply): {
     Eterm* next;
     SWAPOUT;
     next = apply(c_p, r(0), x(1), x(2), reg);
     SWAPIN;
     if (next != NULL) {
         r(0) = reg[0];
         SET_CP(c_p, I+1);
         SET_I(next);
         Dispatch();
     }
     I = handle_error(c_p, I, reg, apply_3);
     goto post_error_handling;
}


static Uint*
apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
{
    int arity;
    Export* ep;
    Eterm tmp, this;

    /*                                                                                                                                                                                                        
     * Check the arguments which should be of the form apply(Module,                                                                                                                                          
     * Function, Arguments) where Function is an atom and                                                                                                                                                     
     * Arguments is an arity long list of terms.                                                                                                                                                              
     */
    if (is_not_atom(function)) {
        /*                                                                                                                                                                                                    
         * No need to test args here -- done below.                                                                                                                                                           
         */
    error:
        p->freason = BADARG;

    error2:
        reg[0] = module;
        reg[1] = function;
        reg[2] = args;
        return 0;
    }

    /* The module argument may be either an atom or an abstract module                                                                                                                                        
     * (currently implemented using tuples, but this might change).                                                                                                                                           
     */
    this = THE_NON_VALUE;
    if (is_not_atom(module)) {
        Eterm* tp;

  if (is_not_tuple(module)) goto error;
        tp = tuple_val(module);
        if (arityval(tp[0]) < 1) goto error;
        this = module;
        module = tp[1];
        if (is_not_atom(module)) goto error;
    }

    /*                                                                                                                                                                                                        
     * Walk down the 3rd parameter of apply (the argument list) and copy                                                                                                                                      
     * the parameters to the x registers (reg[]). If the module argument                                                                                                                                      
     * was an abstract module, add 1 to the function arity and put the                                                                                                                                        
     * module argument in the n+1st x register as a THIS reference.                                                                                                                                           
     */

    tmp = args;
    arity = 0;
    while (is_list(tmp)) {
        if (arity < (MAX_REG - 1)) {
            reg[arity++] = CAR(list_val(tmp));
            tmp = CDR(list_val(tmp));
        } else {
            p->freason = SYSTEM_LIMIT;
            goto error2;
        }
    }
    if (is_not_nil(tmp)) {      /* Must be well-formed list */
        goto error;
    }
    if (this != THE_NON_VALUE) {
        reg[arity++] = this;
    }


...
}

上面的代码就是干我们上面解释的事情的。

结论: 模块参数化完全是个语法糖, 没有任何magic。所以这个特性在hipe下也可以放心大胆用。
分享到:
评论
2 楼 z416177937 2010-08-18  
在这儿我知道了,这种方法如何应用,而且是可以应用的。后面具体的解释以后看,貌似有点麻烦。先知其然吧。
1 楼 argan 2009-09-25  
哈哈,不错,本来只知其然,现在知道一点所以然了

相关推荐

    erlang OTP Design Principles之Gen中文

    Erlang OTP设计原则强调模块化的代码组织,因此通常建议将接口函数(如`start_link/1`和`button/1`)与回调函数放在同一个模块中,以便于理解和维护。在`code_lock`模块中,`init/1`函数初始化状态机,`locked/2`和`...

    erlang四大behaviour之四-supervisor

    为了定义一个Supervisor,我们需要创建一个新的Erlang模块,并声明它遵循`supervisor`行为。具体来说: 1. **模块定义**: ```erlang -module(ch_sup). -behaviour(supervisor). -export([start_link/0]). -...

    Erlang入门:构建application练习5(监督树)

    创建一个基本的supervisor,我们需要定义一个行为模块,实现`behaviour()`声明为`supervisor`,并提供`init/1`回调来初始化进程树。`init/1`回调返回一个tuple,其中包含启动策略和进程定义列表。 ```erlang -...

    Erlang安装手册

    创建一个简单的Erlang模块`tut.erl`,包含基本的打印功能。通过编译和运行该模块来检查Erlang是否正确安装和配置。 以上是对Erlang及其核心特性的详细介绍,希望对您了解和学习Erlang有所帮助。

    kissy模块化实践

    这些语言通过不同的机制实现模块化,如条件预处理、包导入、类定义等,共同推动了模块化编程的发展。 #### 前端代码演变与Kissy模块化实践 前端开发经历了从混沌至现代的演化历程: - **混沌时期**:早期网页混杂...

    getting_started_erlang-5.4.pdf

    Erlang 支持模式匹配,因此一个函数可以有多个实现,根据传入参数的不同而自动选择合适的实现。 ##### 3.3 原子(Atoms) - **原子** 是 Erlang 中的常量类型之一,类似于其他语言中的字符串常量,但性能更高。...

    Erlang Reference Manual

    字符串化宏参数是指将宏参数转换为字符串的过程。手册中详细介绍了字符串化宏参数的方法及应用场景。 ### 1.8 记录 **1.8.1 定义记录** 记录是一种具有命名字段的数据结构,用于表示具有固定字段集的对象。手册中...

    使用rebar工具开发erlang工程项目和发布erlang工程项目借鉴.pdf

    通过以上步骤,开发者可以有效地组织和管理Erlang项目,实现模块化开发,并利用rebar进行自动化构建和测试,提高开发效率。在实际的Web开发中,如nigrogen2框架,rebar同样能帮助开发者轻松管理项目依赖,快速构建...

    E语言源码目录树演示.zip

    2. **src**:源代码文件通常放在这里,以.erl为扩展名,例如`module.erl`代表一个名为`module`的Erlang模块。 3. **include**:这个目录用于放置头文件,通常包含常量定义和类型声明,以.hrl为扩展名。 4. **test*...

    EWSD(V15)命令手册

    EWSD交换机基于模块化设计,包含硬件和软件两大部分。硬件部分包括中央处理器单元(CPU)、存储单元、接口模块、电源模块等。软件部分则由操作系统、应用程序和服务程序组成,这些程序协同工作,实现呼叫处理、路由...

    Python 学习文档

    结构化编程强调的是将程序分解成若干个较小的功能模块,每个模块负责完成特定的任务。这种方法有利于提高代码的可读性和可维护性。面向过程编程是一种基于结构化编程思想的方法论,通过定义不同的函数来实现不同的...

    函数式编程另类指南.pdf

    函数式编程作为一种强大的编程范式,不仅提供了更为简洁优雅的代码结构,还促进了软件开发的模块化和可复用性。尽管存在一定的学习门槛,但对于希望提高代码质量和维护性的开发者来说,掌握函数式编程的基本原理是...

    rabbitmq面试题.pdf

    - 通过设置队列参数来实现消息的优先级排序。 **38. RabbitMQ是否支持多租户(Multi-tenancy)?** - 通过虚拟主机(VHosts)实现多租户功能,隔离不同用户的资源。 #### 六、安全性 **39. RabbitMQ如何实现安全...

    message-service

    - **消息处理器**:包括生产者(Producer)和消费者(Consumer)接口及其实现,它们负责将消息发送到消息队列或将队列中的消息消费掉。 - **消息模型**:定义了业务消息的数据结构,可能包括实体类和序列化/反序列化...

Global site tag (gtag.js) - Google Analytics