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
 
 

 

[Erlang 0120] Know a little Core Erlang的更多相关文章

  1. Core Erlang:Erlang的Core中间表示

    随着erlang的不断发展,它的语法越来越复杂,不便于诸如分析器,调试器此类程序在源码层次直接进行解析,而CORE Erlang旨在为Erlang提供一个人类可读可改的中间表示(Intermediat ...

  2. [Erlang 0126] 我们读过的Erlang论文

    我在Erlang Resources 豆瓣小站上发起了一个征集活动 [链接] ,"[征集] 我们读过的Erlang论文",希望大家来参加.发起这样一个活动的目的是因为Erlang相 ...

  3. [Erlang 0115] 2014值得期待的Erlang两本新书

    在2014年的开头就有这样一个令人振奋的好消息,Erlang有一本新书即将出版 <The Erlang Runtime System>,其作者happi在2013年3月份公布了这本书的写作 ...

  4. [Erlang 0004] Centos 源代码编译 安装 Erlang

    原文地址: http://www.cnblogs.com/me-sa/archive/2011/07/09/erlang0004.html 由于最终部署的生产环境是Centos,所以我需要在Cento ...

  5. [Erlang 0129] Erlang 杂记 VI

    把之前阅读资料的时候记下的东西,整理了一下. Adding special-purpose processor support to the Erlang VM   P23 简单介绍了Erlang C ...

  6. [Erlang 0127] Term sharing in Erlang/OTP 上篇

    之前,在 [Erlang 0126] 我们读过的Erlang论文 提到过下面这篇论文: On Preserving Term Sharing in the Erlang Virtual Machine ...

  7. [Erlang 0125] Know a little Erlang opcode

    Erlang源代码编译为beam文件,代码要经过一系列的过程(见下面的简图),Core Erlang之前已经简单介绍过了Core Erlang,代码转换为Core Erlang,就容易拨开一些语法糖的 ...

  8. [Erlang 0122] Erlang Resources 2014年1月~6月资讯合集

    虽然忙,有些事还是要抽时间做; Erlang Resources 小站 2014年1月~6月资讯合集,方便检索.      小站地址: http://site.douban.com/204209/   ...

  9. Erlang C1500K长连接推送服务-性能

    Whatsapp已经使用Erlang在生产环境跑到96GB内存单机 3M长连接,参加:WhatsApp的Erlang世界.毕竟业务级别能达到Whatsapp那样极少,现在只有千万级,单机太多挂一台影响 ...

随机推荐

  1. WebEssentials 在vs2013 update5安装报错的解决方法.

    WebEssentials 最高支持到update4 如果更新到了update5 RC, 则无法直接安装. 解决方法是 1,下载WebEssentials2013.vsix 文件. 2, 安装7zip ...

  2. 【说解】在shell中通过mkfifo创建命名管道来控制多个进程并发执行

    背景: 工作中有两个异地机房需要传数据,数据全名很规范,在某个目录下命名为统一的前缀加上编号.如/path/from/file.{1..100}.而机房间的专线对单个scp进程的传输速度是有限制的,比 ...

  3. 数据预处理中归一化(Normalization)与损失函数中正则化(Regularization)解惑

    背景:数据挖掘/机器学习中的术语较多,而且我的知识有限.之前一直疑惑正则这个概念.所以写了篇博文梳理下 摘要: 1.正则化(Regularization) 1.1 正则化的目的 1.2 正则化的L1范 ...

  4. 新编码神器Atom使用纪要

    Atom是 Github 专门为程序员推出的一个跨平台文本编辑器.她很大程度上继承了SublimeText的美,而又不仅如此.有费了蛮大力气总结了一篇关于SublimeText文章: 如何优雅地使用S ...

  5. 自用的基于Emit的C#下DataTable转实体类方法

    之前一直在做WebForm的开发,数据绑定时直接DataTable绑定Gridview很方便,但是最近开始往MVC转,数据列表的传递和页面展示基本上是以List为主,像下面这样,遍历实体类的各个字段去 ...

  6. 从Fiddler抓包到Jmeter接口测试(简单的思路)

    备注:本文为博主的同事总结的文章,未经博主允许不得转载. Fiddler下载和配置安装 从网上下载fiddler的安装包即可,直接默认,一直点击下一步,直至安装完成. 安装完成后直接打开Fiddler ...

  7. 解析大型.NET ERP系统核心组件 查询设计器 报表设计器 窗体设计器 工作流设计器 任务计划设计器

    企业管理软件包含一些公共的组件,这些基础的组件在每个新项目立项阶段就必须考虑.核心的稳定不变功能,方便系统开发与维护,也为系统二次开发提供了诸多便利.比如通用权限管理系统,通用附件管理,通用查询等组件 ...

  8. JSON.parse 与 eval() 对于解析json的问题

    1.eval()与JSOn.parse的不同 eval() var c = 1; //全局变量 var jsonstr1 = '{"name":"a",&quo ...

  9. 一行代码引入 ViewPager 无限循环 + 页码显示

    (出处:http://www.cnblogs.com/linguanh) 前序: 网上的这类 ViewPager 很多,但是很多都不够好,体现在 bug多.对少页面不支持,例如1~2张图片.功能整合不 ...

  10. Linux网络相关配置

    一.修改网卡相关配置 Linux网络参数是在/etc/sysconfig/network-scripts/ifcfg-eth0中设置,其中ifcfg-eth0表示是第一个网卡,如果还有另外一块网卡,则 ...