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 再次补充
2002的博客,原文链接:
http://www.cnblogs.com/me-sa/p/know_a_little_core_erlang.html如需转载请自行联系原博主。