
Erlang开发者或多或少都用过或者听说过Core erlang,它是什么样的呢?新建一个测试模块a.erl,如下操作会生成core erlang代码而非a.beam:
1 2 | Eshell V6.0 (abort with ^G) 1> c(a,[to_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/
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 再次补充
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步