论坛首页 综合技术版 FP

一门天生就能损害人眼视力的语言->Erlang

浏览 21892 次
该帖已经被评为精华帖
作者 正文
时间:2008-04-03 关键字: erlang
一门天生就能损害人眼视力的语言->Erlang

受javaEye综合技术版的熏陶,受某位大牛:“Erlang下一个java语言“文章的蛊惑,
学起了函数式编程思想,学起了Erlang。

学习开始于“Getting Started With Erlang”,仅寥寥4小时就学完了,算是入了门,
当时的体会是:函数式编程也就这样,简单之极,搞来搞去都是list、tuple,函数传来传去、套来套去。
入门的顺利促使了进一步学习的冲动,但是越了解越是觉得在受罪。

Ok,憋了20多天啦,常常有想砸死Erlang(恶狼)的冲动。。。

我起初只是对Erlang语言本身及它的编译器实现感兴趣,
一切的不爽都来自于看Erlang语言编译器源码的过程:

故事从compiler/src目录开始,
用自个写的小程序纺计了compiler/src目录下的所有文件的行数,还不到4万行,哇!这么牛X。

找到了切入点:compile.erl
看到了如下代码:
expand_opt(basic_validation, Os) ->
    [no_code_generation,to_pp,binary|Os];
expand_opt(strong_validation, Os) ->
    [no_code_generation,to_kernel,binary|Os];
expand_opt(report, Os) ->
    [report_errors,report_warnings|Os];
expand_opt(return, Os) ->
    [return_errors,return_warnings|Os];
expand_opt(r10, Os) ->
    [no_stack_trimming,no_topt,no_binaries,no_gc_bifs,no_constant_pool|Os];
expand_opt(r11, Os) ->
    [no_stack_trimming,no_binaries,no_constant_pool|Os];
expand_opt({debug_info_key,_}=O, Os) ->
    [encrypt_debug_info,O|Os];
expand_opt(no_binaries=O, Os) ->
    %%Turn off the entire type optimization pass.
    [no_topt,O|Os];
expand_opt(no_float_opt, Os) ->
    %%Turn off the entire type optimization pass.
    [no_topt|Os];
expand_opt(O, Os) -> [O|Os].

眼睛第一次随着“->” “并发/并行“起来,
Ok,经过一阵拐弯抹角之后总算找到了词法分析的源文件stdlib/src/erl_scan.erl,
要是你看过上面的代码后眼睛还是立体的,试试下面:
sub_scan_escape([O1,O2,O3|Cs], [Fun|Stack], Toks, Pos, State, Errors) 
  when O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 ->
    Val = (O1*8 + O2)*8 + O3 - 73*$0,
    Fun([Val|Cs], Stack, Toks, Pos, State, Errors);
sub_scan_escape([O1,O2]=Cs, Stack, Toks, Pos, State, Errors) 
  when O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7 ->
    more(Cs, Stack, Toks, Pos, State, Errors, fun sub_scan_escape/6);
sub_scan_escape([O1,O2|Cs], [Fun|Stack], Toks, Pos, State, Errors) 
  when O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7 ->
    Val = (O1*8 + O2) - 9*$0,
    Fun([Val|Cs], Stack, Toks, Pos, State, Errors);
sub_scan_escape([O1]=Cs, Stack, Toks, Pos, State, Errors) 
  when O1 >= $0, O1 =< $7 ->
    more(Cs, Stack, Toks, Pos, State, Errors, fun sub_scan_escape/6);
sub_scan_escape([O1|Cs], [Fun|Stack], Toks, Pos, State, Errors) 
  when O1 >= $0, O1 =< $7 ->
    Val = O1 - $0,
    Fun([Val|Cs], Stack, Toks, Pos, State, Errors);
%% \^X -> CTL-X
sub_scan_escape([$^,C|Cs], [Fun|Stack], Toks, Pos, State, Errors) ->
    Val = C band 31,
    Fun([Val|Cs], Stack, Toks, Pos, State, Errors);
sub_scan_escape([$^]=Cs, Stack, Toks, Pos, State, Errors) ->
    more(Cs, Stack, Toks, Pos, State, Errors, fun sub_scan_escape/6);
sub_scan_escape([$^|Eof], [Fun|Stack], Toks, Pos, State, Errors) ->
    Fun(Eof, Stack, Toks, Pos, State, Errors);
%% \NL (backslash newline)
sub_scan_escape([$\n|Cs],[Fun|Stack], Toks, Pos, State, Errors) ->
    Fun([nl|Cs], Stack, Toks, Pos, State, Errors);
%% \X - familiar escape sequences
sub_scan_escape([C|Cs], [Fun|Stack], Toks, Pos, State, Errors) ->
    Val = escape_char(C),
    Fun([Val|Cs], Stack, Toks, Pos, State, Errors);
%%
sub_scan_escape([], Stack, Toks, Pos, State, Errors) ->
    more([], Stack, Toks, Pos, State, Errors, fun sub_scan_escape/6);
sub_scan_escape(Eof, [Fun|Stack], Toks, Pos, State, Errors) ->
    Fun(Eof, Stack, Toks, Pos, State, Errors).


好的,接着得转到语法分析啦,找啊找,找到了源头yecc(parsetools/src/yecc.erl)
这回想让你体会一下Erlang的List Comprehensions:
find_user_code(ParseActions, St) ->
    [#user_code{state = State, 
                terminal = Terminal, 
                funname = inlined_function_name(State, Terminal), 
                action = Action} || 
        {State, La_actions} <- ParseActions,
        {Action, Terminals, RuleNmbr, NmbrOfDaughters} 
            <- find_user_code2(La_actions),
        case tokens(RuleNmbr, St) of
            [{var, _, '__1'}] -> NmbrOfDaughters =/= 1;
            _ -> true
        end,
        Terminal <- Terminals].


再快一点,跳到compiler/src/v3_core.erl超前体验一下为各类括号找配偶的“乐趣”
gexpr_test({atom,L,true}, Bools, St0) ->
    {#c_literal{anno=lineno_anno(L, St0),val=true},[],Bools,St0};
gexpr_test({atom,L,false}, Bools, St0) ->
    {#c_literal{anno=lineno_anno(L, St0),val=false},[],Bools,St0};
gexpr_test(E0, Bools0, St0) ->
    {E1,Eps0,St1} = expr(E0, St0),
    %% Generate "top-level" test and argument calls.
    case E1 of
	#icall{anno=Anno,module=#c_literal{val=erlang},name=#c_literal{val=N},args=As} ->
	    Ar = length(As),
	    case erl_internal:type_test(N, Ar) orelse
		erl_internal:comp_op(N, Ar) of
		true -> {E1,Eps0,Bools0,St1};
		false ->
		    Lanno = Anno#a.anno,
		    {New,St2} = new_var(Lanno, St1),
		    Bools = [New|Bools0],
		    {#icall{anno=Anno,	%Must have an #a{}
			    module=#c_literal{anno=Lanno,val=erlang},
			    name=#c_literal{anno=Lanno,val='=:='},
			    args=[New,#c_literal{anno=Lanno,val=true}]},
		     Eps0 ++ [#iset{anno=Anno,var=New,arg=E1}],Bools,St2}
	    end;
	_ ->
	    Anno = get_ianno(E1),
	    Lanno = get_lineno_anno(E1),
	    case core_lib:is_simple(E1) of
		true ->
		    Bools = [E1|Bools0],
		    {#icall{anno=Anno,	%Must have an #a{}
			    module=#c_literal{anno=Lanno,val=erlang},
			    name=#c_literal{anno=Lanno,val='=:='},
			    args=[E1,#c_literal{anno=Lanno,val=true}]},Eps0,Bools,St1};
		false ->
		    {New,St2} = new_var(Lanno, St1),
		    Bools = [New|Bools0],
		    {#icall{anno=Anno,	%Must have an #a{}
			    module=#c_literal{anno=Lanno,val=erlang},
			    name=#c_literal{anno=Lanno,val='=:='},
			    args=[New,#c_literal{anno=Lanno,val=true}]},
		     Eps0 ++ [#iset{anno=Anno,var=New,arg=E1}],Bools,St2}
	    end
    end.




累了,先说这么多,有反对的,下回眼睛休息好了继续砸死Erlang(恶狼)

最后给个Erlang让人超级傻眼的例子(你认为输出是什么?):
-module(record_test).
-export([test/0]).
-record(rec,{f1,f2}).

test()->
Rec2=#rec{f1=2,f2=3},
test2(Rec2),
io:fwrite("Rec2=~p ~n",[Rec2]),

F1=2,
F2=ets:new(rec_f2, [set]),
Rec3 = #rec{f1 =F1, f2=F2},
ets:insert(Rec3#rec.f2, {10}),
io:fwrite("Rec3#rec.f2=~p ~n",[ets:tab2list(Rec3#rec.f2)]),
test3(Rec3),
io:fwrite("Rec3#rec.f2=~p ~n",[ets:tab2list(Rec3#rec.f2)]),
io:fwrite("Rec3=~p ~n",[Rec3]).

test2(Rec)->
Rec#rec{f1=20,f2=30}.

test3(Rec)->
Rec#rec{f1=20,f2=ets:insert(Rec#rec.f2, {30})}.
   
时间:2008-04-04
感觉没有进行很好的排版。我不会用Erlang,所以也不清楚你这些程序是做什么的。不过我不知道是源代码就如此还是你贴上来的时候缩进没了。反正我现在看确实头晕。本来就不知道语法,加上没有缩进看不清结构,要命。
   
0 请登录后投票
时间:2008-04-04
魔力猫咪 写道
感觉没有进行很好的排版。我不会用Erlang,所以也不清楚你这些程序是做什么的。不过我不知道是源代码就如此还是你贴上来的时候缩进没了。反正我现在看确实头晕。本来就不知道语法,加上没有缩进看不清结构,要命。

JavaEye的编辑器没有Erlang语言的代码模板,试了几个样式都是一团糟。

除了最后一个代码例子外,其他代码都是让人看看Eralng语言的代码风格有多糟,
最后一个代码例子想表达的是:Erlang对于记录字段值更新的不一致性。

给几张图吧:
  • E6a64dc7-7716-34dc-b684-4cd409eb1d29-thumb
  • 描述:
  • 大小: 159.6 KB
  • 6c1d7aea-abb9-3d42-9ba6-423e4585e36c-thumb
  • 描述:
  • 大小: 174.9 KB
  • Ad1461d2-8745-3eba-ab39-18389260fce2-thumb
  • 描述:
  • 大小: 145 KB
   
0 请登录后投票
时间:2008-04-04
还有两张图
  • 2617d39f-2d23-3c23-8436-70127f980a4d-thumb
  • 描述: record_test.erl
  • 大小: 104.4 KB
  • Ca54daf9-2113-3dca-8245-d06408cf2008-thumb
  • 描述: v3_core.erl
  • 大小: 158.4 KB
   
0 请登录后投票
时间:2008-04-04
然则如果它能用10行代码就能做其他语言要100行或者200行才能做并且还不敢肯定是不是做对的事情,对视力的影响就要重新评估了
   
0 请登录后投票
时间:2008-04-04
gigix 写道
然则如果它能用10行代码就能做其他语言要100行或者200行才能做并且还不敢肯定是不是做对的事情,对视力的影响就要重新评估了



但注定它不会成为某种流行~~它的命就是和那些Effixxx,Smalltake一样.
   
0 请登录后投票
时间:2008-04-04
是啊是啊,还有些语言只要制造商对外宣传“这是一门搞笑的开发语言”它马上就变成开发语言了。
   
0 请登录后投票
时间:2008-04-04
lichray 写道
是啊是啊,还有些语言只要制造商对外宣传“这是一门搞笑的开发语言”它马上就变成开发语言了。


呵呵,真是好久不见啦,lichray还是依旧,一位FP的虔诚信徒。

我是去年的zhh2007,一晃就一年。

OK,这回复当是握个手.
   
0 请登录后投票
时间:2008-04-04
gigix 写道
然则如果它能用10行代码就能做其他语言要100行或者200行才能做并且还不敢肯定是不是做对的事情,对视力的影响就要重新评估了


这要评估可能就得细分了,

看10000行Erlang代码我花20天,同样的20天我看了50000行Java代码还不伤眼睛,还是轻轻松松看完,
要说这代码量能做多少事,做对多少事,真还得考量考量。
   
0 请登录后投票
时间:2008-04-05
为了进一步展开讨论,也为了迎接函数式编程语言(FP)的忠实信徒的砖头,
我再壮胆一次,再狠砸Erlang一次(间接砸了函数式编程语言)。


当然,我对函数式编程语言的认识可能远远比不上JavaEye里那几位大牛(N>=1年的研究资历),
在我的书架上有关函数式编程语言的书籍主要有两本:
<<ML程序设计教程>>
<<计算机程序的构造和解释>>(也就是FP信徒们常常引以为荣的SICP)
买了一年啦,每本看了还不超过40页,
(为什么?对于咱这种从一开始就受汇编语言、C语言洗礼的人来说FP真的没什么吸引人的)

(题外话:要不是去年跟lich_ray发生口水战,这两本书或许现在都不会出现在我书架上)


除上面两本书外,还有两本程序设计语言原理的书中有些章节提到函数式编程语言,
所以我从书本上学来的函数式编程语言的知识只有几个概念的了解。

直到发这个贴子前的这三个星期,从学习“Getting Started With Erlang”开始,
这20来天的时间算是认真研究了Erlang(官方也声称Erlang是一门函数式编程语言)。

本打算用40天时间分析完Erlang语言的编译器实现代码,
结果三个星期过去了,才看了2/5(现在还在犹豫要不要再往下看),
可以用“活受罪”来形容这三个星期,眼睛每天差不多都是红红的,
看上1个多小时代码就得跑到水龙头下用清水滋润一下双眼,
你可别认为我有眼疾,没有,我好得很,
从UCDOS时代看、写汇编代码开始到Java代码,我都没受过这份罪。

我现在都在怀疑FP信徒们是不是也在内心深处暗骂他们的神灵(Ya的,贼多括号),
除非FP信徒们永远在SICP中意淫,玩弄着教科书中那些玩具般的例子,
时不时再炫耀一番:“瞧,用Scheme实现"非比那次"(Fibonacci)就几行,看Java如何如何。。。“

有篇被传烂的文章叫“4个程序员的一天“,
出处在http://www.cnblogs.com/linkcd/archive/2005/07/19/196087.html
这就是典型的自我陶醉、意淫,
等自己用函数式编程语言至少写上两千行代码,至少看过别人写的一万行代码之后,回头看自己写的东西是不是觉得在搞笑???
发明编程语言不是只为了反反复复写那么10来行的玩具例子!!!


OK,罗嗦了那么多,进入正题,
首先简单说一下接下来要谈论的问题范围:

--------------------------------------------------------
函数式编程语言(主要是Erlang语言)的设计、语法、用户体验
以及Erlang语言的官方编译器实现代码(只涉及一部分),

当然,还有标准库(stdlib)、核心库(Kernel)也可讨论到源代码的地步,

Erlang/OTP的整个运行系统的实现也可以涉及,
不过老实说,我没看过具体的实现代码(mryufeng这位牛人号称有研究),
所以我要参与运行系统的实现的讨论最多只停留在现有的官方文档这个层面。
--------------------------------------------------------
   
0 请登录后投票
论坛首页 综合技术版 FP

跳转论坛:
JavaEye推荐