[Erlang 0120] Know a little Core Erlang

  1. 云栖社区>
  2. 博客>
  3. 正文

[Erlang 0120] Know a little Core Erlang

唐玄奘 2017-12-08 22:20:00 浏览1185
展开阅读全文

  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团队共同维护,项目首页上有两份非常重要的资料:语言规格说明和简介性质的论文
 
[0] An Introduction to Core Erlang http://www.it.uu.se/research/group/hipe/cerl/doc/cerl_intro.ps
[1] Core Erlang Language Specification  http://www.it.uu.se/research/group/hipe/cerl/doc/core_erlang-1.0.3.pdf
 
注:不想下载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如需转载请自行联系原博主。

网友评论

登录后评论
0/500
评论
唐玄奘
+ 关注