[Erlang 0120] Know a little Core Erlang

简介:

  Erlang开发者或多或少都用过或者听说过Core erlang,它是什么样的呢?新建一个测试模块a.erl,如下操作会生成core erlang代码而非a.beam:
 
?
1
2
Eshell V6.0  (abort with ^G)
1> c(a,[to_core]).

 

这时文件夹中已经生成了一个a.core的文件,然后我们如下行事:
 
?
1
2
2> c(a,[from_core]).
{ok,a}

  

 这时已经看到a.beam了,打开a.core的文件,这些看起来熟悉却又有点奇怪的代码是什么意思?有什么用呢?

 

What is Core Erlang?

 

   Core Erlang 项目地址:  http://www.it.uu.se/research/group/hipe/cerl/ 

 

 
   目前Core Erlang项目由HIPE和OTP团队共同维护,项目首页上有两份非常重要的资料:语言规格说明和简介性质的论文
 
 
注:不想下载Postscript文件阅读器的,可以在线转换ps到PDF  http://www.ps2pdf.com/

 

 

Why Core Erlang ?

  

   Core Erlang 是Erlang的一种中间表现形式,Erlang在语法层面一直在演变,越发复杂,一些代码分析工具或者调试工具解读代码就不方便了.Core Erlang就是因此而生,它尽可能的保持语法简单,稳定以方便工具解析,同时具备代码可读性以方便手工修改代码.

 

   换句话说,通过Core Erlang我们可以透过语法糖看到真实的代码逻辑是怎样的,在之前分析Erlang语法相关的博文中我就数次使用Core Erlang,比如:

 

[Erlang 0034] Erlang iolist  通过Core Erlang 查看iolists内部表示

http://www.cnblogs.com/me-sa/archive/2012/01/31/erlang0034.html

 

[Erlang 0039] Erlang Inheritance 通过Core Erlang看所谓的继承extends实际上是做了什么

http://www.cnblogs.com/me-sa/archive/2012/02/17/erlang0039.html

 

[Erlang 0058] Erlang Function调用效率 通过Core Erlang看几种函数调用方式性能差异是如何产生的

http://www.cnblogs.com/me-sa/archive/2012/05/06/erlang-function-call-efficiency.html

 

Core Erlang in Action

 

   下面我将通过几段代码把常用的Core Erlang展示一下,模块定义和attitudes之类的基本上都能对应上就不说了,不求完备,但求实用,直接进入方法.准备好伙计,要开始战斗了!

 

 

 

第一段代码

 

?
1
2
3
4
5
6
7
8
append_list()->
[a,b] ++ [1,2].
 
append_list2(L)->
    [100,200] ++L.
 
append_list3(L) ->
    L++[100,200].

  

对应的Core Erlang代码:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
'append_list' /0 =
     %% Line 5
     fun () ->
      [ 'a' |[ 'b' |[1|[2]]]]
'append_list2' /1 =
     %% Line 8
     fun (_cor0) ->
      %% Line 9
      [100|[200|_cor0]]
'append_list3' /1 =
     %% Line 11
     fun (_cor0) ->
      %% Line 12
      call 'erlang' : '++'
          (_cor0, [100|[200]])

   

   这里就已经很好玩了对不对,所谓的函数,其实就是把lambda表达式(或者说Fun)赋值给变量.然后看append_list()由于结果是可以编译时计算出来的,所以做了优化,直接给出了结果.append_list2(L)也做了优化把两个元素挂在了表头.append_list3(L)没有什么优化余地老老实实call 'erlang':'++'

 

第二段代码

 

?
1
2
3
4
5
6
test()->
    A=lists:seq(1,10),
    B={1,2,3,4},
    C= << "42" >>,
    {M,N,P,Q} =B,
    {[A,M],[P,Q],N,C}.

  

可以猜测一下这段代码对应的Core Erlang是什么样的?我把答案代码折叠一下

 

?
1
2
3
4
5
6
7
8
9
10
'test' /0 =
     %% Line 14
     fun () ->
      let <A> =
          %% Line 15
          call 'lists' : 'seq'
           (1, 10)
      in  %% Line 19
          {[A|[1]],[3|[4]],2,#{#<52>(8,1, 'integer' ,[ 'unsigned' |[ 'big' ]]),
                     #<50>(8,1,'integer',['unsigned'|['big']])}#}

  

这里我们特别要关注两点:1. let原语显示指定了变量的作用范围,是不是想到了下面的代码?

 

?
1
2
3
4
5
6
7
8
(define-syntax let
   (syntax-rules ()
     (( let (( var expr) ...) body ...)
       ((lambda ( var ...) body ...) expr ...))))
 
  
 
( let ((a 1) (b 2)) (+ a b))

  

2. 二进制数据<<"42">>在Core Erlang表达的时候会把默认的一些元数据描述出来,程序解析当然方便,人工阅读就显得繁琐了.

 

第三段代码

 

第三段代码纯粹是为了演示故意复杂化的,估计没有谁会会这样多此一举的写加法运算吧

 

?
1
2
3
4
5
6
add(A,B)->
case {A,B} of
   {1,1} -> 2;
   {0,0}-> 0;
   {A,B} ->A +B
end.

  

 

Core Erlang代码就有趣的多了,不要被下面这堆东西吓到:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
'add' /2 =
     %% Line 21
     fun (_cor1,_cor0) ->
      %% Line 22
      case <_cor1,_cor0> of
        %% Line 23
        <1,1> when 'true' ->
            2
        %% Line 24
        <0,0> when 'true' ->
            0
        %% Line 25
        <_cor5,_cor6>
            when let <_cor7> =
               call 'erlang' : '=:='
                (_cor5, _cor1)
           in  let <_cor8> =
                call 'erlang' : '=:='
                    (_cor6, _cor0)
               in  call 'erlang' : 'and'
                    (_cor7, _cor8) ->
            call 'erlang' : '+'
             (_cor1, _cor0)
        ( <_fol6,_fol7> when 'true' ->
           let <_cor2> = {_fol6,_fol7}
           in  primop 'match_fail'
                ({ 'case_clause' ,_cor2})
          -| [ 'compiler_generated' ] )
      end

  

   前面两个逻辑分支需要解释一下的就是match pattern的语法结构是<v1,v2>;需要仔细看的是第三个逻辑分支,可以看到模式匹配的细节其实是: _cor7 = (_cor5 =:=  _cor1), _cor8=((_cor6 =:=_cor0)),_cor7 and _cor8;并且后面还有编译期间自动生成的match_fail代码.

 

第四段代码

 

 加强一下对match pattern的印象,看下面这段代码,够简单了吧,生成的Core Erlang代码同样会把逻辑补全:

 

?
1
2
3
match_test(T)->
   {A,B,C} =T,
   [A,{B,C}].

  

 

下一次我们看到模式匹配的时候,脑海中应该能浮现出下面的场景了吧:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
'match_test' /1 =
     %% Line 28
     fun (_cor0) ->
      %% Line 29
      case _cor0 of
        <{A,B,C}> when 'true' ->
            %% Line 30
            [A|[{B,C}|[]]]
        ( <_cor1> when 'true' ->
           primop 'match_fail'
               ({ 'badmatch' ,_cor1})
          -| [ 'compiler_generated' ] )
      end

  

 

第五段代码

 

  我是列表解析的重度使用患者,特别是在Erlang Shell中,我把它当做循环,当做过滤器,当做if;当它转换成Core Erlang表示的时候,就呈现出其背后的机制:

?
1
2
lc_test()->
    [Item * 2  || Item <- lists:seq(1,20),Item rem 2==0].

  

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
'lc_test' /0 =
     %% Line 32
     fun () ->
      %% Line 33
      ( letrec
            'lc$^0' /1 =
             fun (_cor4) ->
                 case _cor4 of
                <[Item|_cor1]>
                    when try
                       let <_cor2> =
                           call 'erlang' : 'rem'
                            (Item, 2)
                       in  call 'erlang' : '=='
                            (_cor2, 0)
                      of <Try> ->
                       Try
                      catch <T,R> ->
                       'false' ->
                    let <_cor5> =
                     call 'erlang' : '*'
                         (Item, 2)
                    in  let <_cor6> =
                         apply 'lc$^0' /1
                          (_cor1)
                     in  ( [_cor5|_cor6]
                           -| [ 'compiler_generated' ] )
                ( <[Item|_cor1]> when 'true' ->
                      apply 'lc$^0' /1
                       (_cor1)
                  -| [ 'compiler_generated' ] )
                <[]> when 'true' ->
                    []
                ( <_cor4> when 'true' ->
                      ( primop 'match_fail'
                         ({ 'function_clause' ,_cor4})
                     -| [{ 'function_name' ,{ 'lc$^0' ,1}}] )
                  -| [ 'compiler_generated' ] )
                 end
        in  let <_cor3> =
             call 'lists' : 'seq'
                 (1, 20)
            in  apply 'lc$^0' /1
                 (_cor3)
        -| [ 'list_comprehension' ] )

  

这里要说的就是letrec 它让我们能够在 'lc$^0'/1内部调用 'lc$^0'/1自身.有兴趣的可以找更多关于letrec lisp的资料来看.

 

第六段代码

 

这段代码主要关注尾递归和Guard

 

?
1
2
3
4
fact(N) when N>0 ->  
     N * fact(N-1);   
fact(0) ->          
     1. 

  

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
'fact' /1 =
 
     %% Line 35
 
     fun (_cor0) ->
 
      case _cor0 of
 
        <N>
 
            when call 'erlang' : '>'
 
               (_cor0,
 
                0) ->
 
            let <_cor1> =
 
             %% Line 36
 
             call 'erlang' : '-'
 
                 (N, 1)
 
            in  let <_cor2> =
 
                 %% Line 36
 
                 apply 'fact' /1
 
                  (_cor1)
 
             in  %% Line 36
 
                 call 'erlang' : '*'
 
                  (N, _cor2)
 
        %% Line 37
 
        <0> when 'true' ->
 
            %% Line 38
 
            1
 
        ( <_cor3> when 'true' ->
 
           ( primop 'match_fail'
 
                 ({ 'function_clause' ,_cor3})
 
             -| [{ 'function_name' ,{ 'fact' ,1}}] )
 
          -| [ 'compiler_generated' ] )
 
      end

  

 

 

第七段代码

 

看看所谓的函数分支是什么

 

?
1
2
3
dump(a)->atom_a;
dump([]) ->empty_list;
dump(C)->io:format( "parameter is : ~p" ,[C]).

  

 

看下面的代码,其实所谓逻辑分支其实只是case语句中的逻辑分支而已,只不过要是在项目中写这样冗长的代码估计要疯掉了;语法上支持函数分支让我们可以写短函数,人工维护起来方便些.

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
'dump' /1 =
     %% Line 40
     fun (_cor0) ->
      case _cor0 of
        < 'a' > when 'true' ->
            'atom_a'
        %% Line 41
        <[]> when 'true' ->
            'empty_list'
        %% Line 42
        <C> when 'true' ->
            call 'io' : 'format'
             ([112|[97|[114|[97|[109|[101|[116|[101|[114|[32|[105|[115|[32|[58|[32|[126|[112]]]]]]]]]]]]]]]]], [C|[]])
      end

  

第八段代码

 

当然少不了receive语句了

 

?
1
2
3
4
5
6
7
recv_test()->
   receive
        a-> "a" ;
        m->io:format( "Call M(),Result: ~p " ,[m()]),recv_test();
        {1,2} ->one_two;
        H -> io:format( "recv ~p" ,[H]),recv_test()
   end.

  

 

看下面Core Erlang最后几句是不是恍然大悟,原来是这样啊

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
'recv_test' /0 =
     %% Line 44
     fun () ->
      %% Line 45
      receive
        %% Line 46
        < 'a' > when 'true' ->
            [97]
        %% Line 47
        < 'm' > when 'true' ->
            let <_cor0> =
             apply 'm' /0
                 ()
            in  do  call 'io' : 'format'
                  ([67|[97|[108|[108|[32|[77|[40|[41|[44|[82|[101|[115|[117|[108|[116|[58|[32|[126|[112|[32]]]]]]]]]]]]]]]]]]]], [_cor0|[]])
                 apply 'recv_test' /0
                  ()
        %% Line 48
        <{1,2}> when 'true' ->
            'one_two'
        %% Line 49
        <H> when 'true' ->
            do  call 'io' : 'format'
                 ([114|[101|[99|[118|[32|[126|[112]]]]]]], [H|[]])
             apply 'recv_test' /0
                 ()
      after 'infinity' ->
        'true'

  

第九段代码

 

?
1
2
3
4
5
6
7
8
9
10
-record(person,{id=0,name}).
 
  
 
r(#person{id= ID ,name=Name} =P)->
   {ID,Name}.
 
r_test()->
    P=#person{id=123  , name= "zen" },
    r(P).    

  

 

这下看清楚record是什么了吧?

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
'r' /1 =
     %% Line 56
     fun (_cor0) ->
      case _cor0 of
        <P = { 'person' ,ID,Name}> when 'true' ->
            %% Line 57
            {ID,Name}
        ( <_cor1> when 'true' ->
           ( primop 'match_fail'
                 ({ 'function_clause' ,_cor1})
             -| [{ 'function_name' ,{ 'r' ,1}}] )
          -| [ 'compiler_generated' ] )
      end
'r_test' /0 =
     %% Line 59
     fun () ->
      %% Line 61
      apply 'r' /1
          ({ 'person' ,123,[122|[101|[110]]]})

  

 

第十段代码 

 

这一段应该算是赶潮流的代码,文档里面暂时还没有提到的Maps

 

?
1
2
3
4
5
6
m()->
   M=#{1=>2 , a=>4,{100,200}=>[1,2,3],<< "zen" >> => "Hello" },
   #{{100,200} := Data} =M,
   Data.

  

哇,Maps的Core Erlang表示还真是.....有些人又要说Erlang伤眼睛了

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
'm' /0 =
     %% Line 63
     fun () ->
      let <_cor0> =
          %% Line 64
          ~{::<1,2>,::< 'a' ,4>,::<{100,200},[1|[2|[3]]]>,::<#{#<122>(8,1, 'integer' ,[ 'unsigned' |[ 'big' ]]),
                                        #<101>(8,1,'integer',['unsigned'|['big']]),
                                        #<110>(8,1,'integer',['unsigned'|['big']])}#,[72|[101|[108|[108|[111]]]]]>}~
      in  %% Line 65
          case _cor0 of
            <~{~<{100,200},Data>}~> when 'true' ->
             %% Line 66
             Data
            ( <_cor2> when 'true' ->
               primop 'match_fail'
                ({ 'badmatch' ,_cor2})
           -| [ 'compiler_generated' ] )
          end

  

   看过了上面的代码,我们可以想想Erlang在语法层面做了哪些设计让我们更容易表达想法,代码更简单,好了,就到这里了,假期愉快.

 

2014-4-10 10:41:08 补充

http://www.erlang.org/download/otp_src_17.0.readme

   OTP-11547  The .core and .S extensions are now documented in the erlc  documentation, and the 'from_core' and 'from_asm' options are now documented in the compiler documentation. (Thanks to  Tuncer Ayaz.)

2014-10-21 14:38:56 再次补充

Abstraction and Model Checking of Core Erlang Programs in Maude
 
 
PHP-CoreErlang
PHP-CoreErlang is a DSL (domain specific language) for PHP, which generates "Core Erlang" .core files, which allows one to target the Erlang VM.
 
 
Erlang Types, Abstract Form & Core
 
 
Write A Template Compiler For Erlang
 
 
Implementing languages on the Erlang VM

2002的博客,原文链接:

http://www.cnblogs.com/me-sa/p/know_a_little_core_erlang.html如需转载请自行联系原博主。

目录
相关文章
|
Shell Perl
erlang 小技巧总结
开一个页面总结一些erlang的使用技巧,随时添加新的技巧。
|
消息中间件 网络协议 Go
[Erlang 0123] Erlang EPMD
 epmd进程和Erlang节点进程如影随形,在Rabbitmq集群,Ejabberd集群,Couchbase集群产品文档中都会有相当多的内容讲epmd,epmd是什么呢?   epmd 是Erlang Port Mapper Daemon的缩写,全称足够明确表达它的功能了(相比之下,OTP就.
2004 0
|
编解码 计算机视觉 存储
|
网络协议 Shell Go
|
JavaScript 前端开发 Java