• [Erlang 0120] Know a little Core Erlang



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

     

    这时文件夹中已经生成了一个a.core的文件,然后我们如下行事:
     
    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之类的基本上都能对应上就不说了,不求完备,但求实用,直接进入方法.准备好伙计,要开始战斗了!

     

    第一段代码

    append_list()->
    [a,b] ++ [1,2].
    
    append_list2(L)->
       [100,200] ++L.
    
    append_list3(L) ->
       L++[100,200].
    

      

    对应的Core Erlang代码:

    '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':'++'

    第二段代码

    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是什么样的?我把答案代码折叠一下

    '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原语显示指定了变量的作用范围,是不是想到了下面的代码?

    (define-syntax let
      (syntax-rules ()
        ((let ((var expr) ...) body ...)
          ((lambda (var ...) body ...) expr ...))))
    
     
    
    (let ((a 1) (b 2)) (+ a b))
    

      

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

    第三段代码

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

    add(A,B)->
    case {A,B} of
      {1,1} -> 2;
      {0,0}-> 0;
      {A,B} ->A +B
    end. 
    

      

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

    '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代码同样会把逻辑补全:

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

      

     

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

    '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表示的时候,就呈现出其背后的机制:

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

      

    '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

    fact(N) when N>0 ->   
        N * fact(N-1);    
    fact(0) ->           
        1.  
    

      

    '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 
    

      

     

    第七段代码

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

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

      

     

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

    '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语句了

    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最后几句是不是恍然大悟,原来是这样啊

    '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'
    
     
    

      

    第九段代码

    -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是什么了吧?

    '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

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

      

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

    '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
     
     

     

  • 相关阅读:
    cookies和re
    2018福大软工实践-第一次作业之自我介绍
    2018福大软工实践-第一次作业
    Linux shell脚本编程
    课程第八次作业
    课程第七次作业
    课程第六次作业
    课程第五次作业
    四则运算课程第四次作业
    第三次课程作业
  • 原文地址:https://www.cnblogs.com/me-sa/p/know_a_little_core_erlang.html
Copyright © 2020-2023  润新知