【转载】Erlang精彩讨论-回“老赵”关于“Erlang中最大的问题”

来源:互联网 发布:mac版微信开发工具 编辑:程序博客网 时间:2024/05/21 07:10

原文:http://erlang-china.org/study/puzzle-in-erlang_pattern_match.html/comment-page-1#comments

            http://www.douban.com/group/topic/11354877/


回“老赵”关于“Erlang中最大的问题” 
July 10th, 2009 :: jackyz 
Goto comments Leave a comment 
活跃在博客园的“老赵”,是一位研究 .NET 非常深入的同学(因为我本人也是老赵——jackyz.zhao,所以,特地加了引号)。他最近很关注“在 .NET 下实现 Erlang 语言特性”的课题,并为此写了一系列的技术文章,相当不错,我一直都在关注。他自己写了一个名为 ActorLite 的小东西,此前做过介绍,是个不错的尝试。 
最近“老赵”同学写了一篇《一种适合C# Actor的消息执行方式(上)》,其中提到“(在消息执行上) Erlang 中最大的问题”。这是一个很有意思的观点,而且因为富于代表性因而显得很有价值,很有必要拿出来和大家探讨。 

其中提到: 
Erlang的优势与缺陷 
  Erlang在消息执行方式上的优势在于灵活。Erlang是弱类型语言,在实现的时候可以任意调整消息的内容,或是模式的要求。在 Erlang进行模式匹配时往往有种约定:使用“原子”来表示“做什么”,而使用“绑定”来获取操作所需要的“数据”,这种方式避免了冗余的cast和赋 值,在使用的时候颇为灵活。然而,世上没有完美的事物,Erlang的消息执行方式也有缺陷,而且是较为明显的缺陷。 
  首先,Erlang的数据抽象能力实在太弱。如果编写一个略显复杂的应用程序,您会发现程序里充斥着复杂的元组。您可能会疲于应对那些拥有7、 8个单元(甚至跟多)的元组,一个一个数过来到底某个绑定匹配的是第几项,它的含义究竟是什么——一旦搞错,程序便会出错,而且想要调试都较为困难。因 此,也有人戏称Erlang是一门“天生会损害人视力的语言”(令人惊讶的是,那篇文章居然搜不到了,我们只能从搜索引擎上看出点痕迹了)。 
  而我认为,这并不是Erlang语言中最大的问题,Erlang中最大的问题也是其“弱类型”特性。例如,现在有一个公用的Service Locator服务,任意类型的Actor都会像SL发送一个消息用于请求某个Service的位置,SL会在得到请求之后,向请求方发送一条消息表示应 答。试想,如果SL的功能需要有所修改,作为回复的消息结构产生了变化,那么我们势必要修改每一个请求方中所匹配的模式。由于消息的发送方和接受方在实际 上完全分离,没有基于任何协议,因此静态检查几乎无从做起。一旦遇到这种需要大规模的修改的情况,Erlang程序便很容易产生差错。因为一旦有所遗漏, 系统便无法正常执行下去了。 
这的确是对于动态类型语言很常见到的担心,而且,确实,如果不加注意会成为严重的问题。这种困扰确实是因为 Erlang 的“动态类型”和“基于消息”而造成的。但,这并非无解,实际上,在 Erlang 编程规范之中,已经给出了解决方案。 
通常而言,Erlang 中的消息应该是以“控制流”为主,在消息的数据结构表达上,不同的消息通常都对应着不同的处理流程,在这里“控制”是消息中最为关注的内容。为此我们可以简单的引入一个 atom 来予以表达,这就是 Tag Messages 的最佳实践: 
5.7 Tag messages 
All messages should be tagged. This makes the order in the receive statement less important and the implementation of new messages easier. 
Don’t program like this: 
loop(State) -> 
receive 
... 
{Mod, Funcs, Args} -> % Don't do this 
apply(Mod, Funcs, Args}, 
loop(State); 
... 
end. 
The new message {get_status_info, From, Option} will introduce a conflict if it is placed below the {Mod, Func, Args} message. 
If messages are synchronous, the return message should be tagged with a new atom, describing the returned message. Example: if the incoming message is tagged get_status_info, the returned message could be tagged status_info. One reason for choosing different tags is to make debugging easier. 
This is a good solution: 
loop(State) -> 
receive 
... 
{execute, Mod, Funcs, Args} -> % Use a tagged message. 
apply(Mod, Funcs, Args}, 
loop(State); 
{get_status_info, From, Option} -> 
From ! {status_info, get_status_info(Option, State)}, 
loop(State); 
... 
end. 
可以相信,一段庞杂的代码,在经过一番这样对 Message 本身的设计和重构之后,最终都能让其恢复“秩序与美感”。而消息中的其他参数,如果其处理逻辑非常复杂的话,那么带有模式匹配的子函数,又或着是 Tuple/Record 正是其用武之地。 
上面我们提到了“对 Message 的设计和重构”,实际上,这只是问题的一个方面。另外一个方面是:无论 Message 的“协议”设计得有多精巧,其对外的接口都不应该用消息来表达,而应该是“接口函数”的形式。 
Erlang 系统之中的消息是极度灵活的系统,但它并不适合用来作为模块之间的接口,因为它过于灵活。更加适合这一角色的设施是“函数”。用来充当这样角色的函数就被 称作“接口函数”,在其实现代码中,除了“按照约定的格式发消息以外”什么别的也不做——因为它包装的正是以消息为载体的交互“协议”。它是函数,有着严 格的语法检查,而且可以在多个模块之间重用。一旦需要修改消息协议,起隔离作用的“接口函数”就会体现出其存在的巨大价值。 
5.10 Interface functions 
Use functions for interfaces whenever possible, avoid sending messages directly. Encapsulate message passing into interface functions. There are cases where you can’t do this. 
The message protocol is internal information and should be hidden to other modules. 
Example of interface function: 
-module(fileserver). 
-export([start/0, stop/0, open_file/1, ...]). 

open_file(FileName) -> 
fileserver ! {open_file_request, FileName}, 
receive 
{open_file_response, Result} -> Result 
end. 

...code... 
Erlang 是动态语言,对其进行静态检查比较困难(并非不能 R13 已经有所动作)。但,并不是说没有静态检查就会寸步难行。Erlang 同样重要的语法特性是它还是函数式语言,遇到有疑惑的地方,不妨以函数的角度来思考。 
感谢 Arbow 的分享。感谢 JeffreyZhao 的写作。 
Erlang 语言素有“难学”的名声,其中一个原因就是因为虽然其语法十分简单,但常会让人心生 “就这样了,然后呢?” 之惑——因为过于灵活而无所适从,而且也不存在着显而易见的正确用法。这种困扰通常要在有了一定的实践经验之后才会渐渐消散。换句话说,在“学会”和“用 好”之间存在着一堵模模糊糊的墙,而这一“未知区域”又需要一定的耐心和经验方可穿越。 
study best_practise, language, pattern_match 
Comments (141) Trackbacks (0) Leave a comment Trackback 
1.
Jeffrey Zhao 
July 10th, 2009 at 16:13 | #1 
Reply | Quote 
多谢补充! 
我对Erlang的了解仅到“知道可以做什么”,“怎么做”,除此之外还没有任何实践,有什么问题多多指出。:) 
我的理解是,tag message即使用atom来标记“做什么”,Programming Erlang里也始终贯彻这一点,但是它还是无法约束做这个事情需要哪些“数据”。 
例如open_file函数,它需要receive {open_file_response, Result},但是如果以后它要修改为receive {open_file_response, Result, NewResult},这样所有调用open_file函数的模块都需要修改了。 
我的文章也主要是指这个意思,不知道在实践中该如何应对这个情况。 
2.
liancheng 
July 10th, 2009 at 20:20 | #2 
Reply | Quote 
@Jeffrey Zhao 
这种情况正是接口函数发挥作用的地方。你的用例中发生变更的是通讯协议,而接口函数正是封装通讯协议的地方。向用户暴露接口函数而不是通讯协议,同时尽可 能在不更改接口函数的情况下修改协议,一定程度上可以解决你的问题。作为用户,不应该去试图了解通讯协议并按照通讯协议去自行编写客户端函数,而是应该使 用接口函数来访问服务。如果接口函数也不能保证不变性,我想那应该就不是Erlang的问题了,而是如何设计稳定、可扩展API接口的问题了。 jackyz.zhao应该也是这个意思吧? 
3.
Jeffrey Zhao 
July 10th, 2009 at 23:00 | #3 
Reply | Quote 
@liancheng 
那么,对于我提出的情况,一个良好的设计应该是什么样子的呢?例如我现在的确修改了通讯协议,但是这有时候是不得不修改的。事实上对于Erlang这种没有严格约束的语言,任何process之间的通讯都是在约定,如果有一方要改变约定,又该如何是好呢? 
再者,例如这个示例,open_file的作用是“发起”操作,因此我们可以用接口函数。但是我们的“应答”是被动的,开发人员又如何准备另一个接口函数,用于接收应答消息? 
4.
dsun 
July 11th, 2009 at 14:04 | #4 
Reply | Quote 
实在是忍不住了,说两句吧,呵呵! 
1、Erlang是强类型语言,超强类型的语言,可以去google一下弱类型的定义。receive时是基于pattern matching进行消息接收的,这个有个学名叫做selective receive,如果没有做过超级复杂的状态机,是根本无法理解其在保证状态机概念完整性和防止状态爆炸方面所带来的巨大优势的,我记得Ulf Wiger还专门写过一个PPT。另外,pattern matching也使得OO语言中所谓的多态语法变得无比丑陋,繁杂。 
2、如果你用imperative的视角去应用functional世界中的设施,当然会觉得别扭,元组中项的多少和数据抽象也根本没啥关系。再说 了,由于自己问题没有理解清楚,搞那么多项出来,咋能够怪Erlang呢。说道调试,我编Erlang程序也有n年了(n>4),还从来没有怎么调 试过程序,为啥?因为首先使用Erlang编程很少出bug,即使出了,大部分的bug就在出错的地方,一目了然,而不像其他语言中的距离十万八千里。建 议看看《purely functional data structures》这本书。 
3、实在是不理解举得那个Service Loader的问题是想说明啥,基于消息通信的双方必然基于一定的协议(不管你有没有意识到),这个协议是脱离语言的,包括语法和语义两部分构成。在实现 时,语法部分反映到语言的结构定义上,语义部分反映到流程控制中。更改数据结构其实就是更改了协议,由于消息通信是一个运行时的动态过程(还涉及到消息的 marshalling这种低级操作),所谓的静态检查也起不了多大的作用。这个问题是一个系统设计问题,也就是协议的语义检查和版本兼容设计问题,协议 改了,就等于是协议版本升级了,此时更应该考虑的问题是如何平滑的进行协议升级和协议版本兼容的问题,因为可能已经有成千上万的老节点在运行着呢。Joe Armstrong一直在致力于protocol check的工作,甚至后悔当时没把这个作为first class的语言特性(可以google)。我还没见过那个语言、系统平台在这个问题上的支持能超过Erlang/OTP的的。说道出错,Erlang最 不怕的就是出错,因为其就是容错的,出错立即重启出错的进程,分析错误,更改,热升级,一气呵成。换成其他语言平台,早玩完了。这就是Erlang的哲 学,承认永远无法避免错误,所以造就了Erlang的超级容错特性 
最后再说两句,最近出现了很多模拟erlang并发特性的探讨和实践,我想说的是,如果不能理解并做到Erlang对于软件容错的支持,这些模拟只是个皮毛形式罢了。 
5.
sw2wolf 
July 11th, 2009 at 17:22 | #5 
Reply | Quote 
同样是FP语言, HASKELL比ERLANG优雅, 能否用HASKELL实现 Erlang 语言特性呢? 
6.
dsun 
July 11th, 2009 at 18:48 | #6 
Reply | Quote 
@sw2wolf 
其实functional不functional不是Erlang的重点,按照Joe Armstrong的话来说,Erlang最核心的就是其基于软件容错之上的,pure message passing style的进程模型,进程就是个黑盒子,里面放啥都行,只是放了个functional语言更美味一些,呵呵。 
7.
Jeffrey Zhao 
July 11th, 2009 at 22:05 | #7 
Reply | Quote 
@dsun 
1、能不能给出相关资料呢?我查到的资料,没有一个不说明Erlang是弱类型语言的。 
2、Erlang的抽象能力差也是公认的吧,即时是Programming Erlang的例子里,也见过元素数量很多的元组。至于bug方面,我没有敢想,就不发表意见了,呵呵。 
3、其实你说的和我是统一的,Joe一直致力于protocal check,后悔没有将其作为first class语言特性,不就是说明Erlang这方面有缺陷吗?当然,可以方便的进行热升级,的确是Erlang内置的优秀特性,其他平台虽然并非说做不 到,但是的确不如内置平台特性高效。 
最后你的看法我非常同意,其实我认为“容错”才是Erlang的关键,而“并发”部分其他语言通过简单的框架都可以实现。 
8.
jackyz 
July 12th, 2009 at 00:48 | #8 
Reply | Quote 
晕死,出去一天没上网,这么热闹的回帖都错过了。呵呵。 
@liancheng,就是你说的这个意思。 
@Jeffrey Zhao,照你的这个例子,我再罗嗦一下。 
例如 open_file 函数,它需要 receive {open_file_response, Result},但是如果以后它要修改为 receive {open_file_response, Result, NewResult},这样所有调用 open_file 函数的模块都需要修改了。 
对外而言,你的 Module 所能提供的全部功能就是 Export 出来的那几个 Function ,作为调用者,除了那几个 Function 之外,不需要了解其他的任何信息。 
在 open_file 这个例子里,在设计之初,你就需要确定 open_file 会返回什么值,这是接口设计(API)的工作。也就是说,你要在设计阶段确定好这个函数的外部表现是怎样的。具体他是 receive 某个消息也好,或者是代理到其他的模块也好,这都是在实现阶段才去考虑的问题,和他的外部表现无关。一个设计良好的接口,应该是相对稳定的。换句话说,设 计出来的接口,应该表述相对稳定的“业务逻辑”,而不是“实现逻辑”。 
现在,比如说,你设计好了,最终还是发生了什么事,还是需要改变,这时会有两种情况:
1,内部的协议发生了改变(实现变了),而外部的表现不变(业务表述不变),这时你只管改 open_file 的实现代码就好,外部调用 open_file 的代码你不用改。这是 Interface Function 的要义。 
2,外部的表现发生了改变(业务表述都变化了)。这时其实是一个接口的变更,这时,所有调用 open_file 的地方都需要跟着改,这无法避免,无论你在 Java 里也好 .NET 里也好,都是一样的 Erlang 也没什么不同。 
这里需要注意的是,此时的接口改变,其内在动力并不一定就是因为协议(实现层面的事情)发生了变化,这两者之间并不存在必然的联系。 
让我们更正一个说法:Erlang 是动态类型,而不是弱类型。在实际使用中,确实会大量的碰到往一个元组/记录里塞很多东西的情况,但我认为,这不表示它的抽象能力差,而是相反。而且,如果正确使用语法设施的话,你也不会觉得繁琐。 
9.
dsun 
July 12th, 2009 at 11:42 | #9 
Reply | Quote 
@Jeffrey Zhao 
Joe Armstrong说后悔,是指protocol应该做为语言的first class特性,而不是后来通过库来实现,而且他所关注的是有关protocol的动态语义检查,而不是静态检查。 
另外,所谓用接口来屏蔽协议是一个小的设计问题,大家可以考虑一下一个更大范围的设计问题,比如:我们现在有个分布式系统,有N个节点组成,它们之 间通过某种协议通信,现在要升级系统,协议也要更改,我们一开始该如何设计protocol和升级流程,以保证可以在系统不停止提供服务的情况下安全、平 滑、兼容的进行整个系统的升级呢? 
10.
dsun 
July 12th, 2009 at 11:56 | #10 
Reply | Quote 
说到底,所谓的高并发、伸缩、在线动态升级其根本都是一个软件容错问题,容错问题解决好了,这些东西都是自然地副产品,否则就只能每个做一套,违反DRY,呵呵。 
11.
Caoyuan 
July 12th, 2009 at 13:47 | #11 
Reply | Quote 
Erlang是动态类型的,加上一点点的运行时类型检查和分发(通过is_float, is_record等类型检查函数和内生的atom, tuple, List等少量类型)。 
Erlang的这一特点决定了Erlang不适用于大型的、具有复杂业务逻辑的企业业务系统。协议、函数的接口在这种情况下时需要经常重构的,也意味着需要被改变,这时就会非常非常头疼。 
Erlang的发展方向必然是逐渐加入类型定义,现在是可选项: 
http://www.erlang.org/eeps/eep-0008.html 
比如: 
-spec Function(ArgName1 :: Type1, …, ArgNameN :: TypeN) -> RT. 
-type orddict(Key, Val) :: [{Key, Val}]. 
这些新加入的spec和type属性已经非常接近类型声明了,但,于Java,Scala的类型体系相比,显然是不成体系,以及是不完备的,有些复杂类型,比如继承、mixin的类型无法用这些声明来表达。 
在Erlang的mailing list上,甚至开始考虑更多的加入编译期的类型检查,比如: 
-fun(a::Type1, b::Type2) -> …. 
这样的语言演化与Python的可选类型更接近了。 
对于计算密集的任务,Erlang现在还有另一个致命的弱点,就是它的processes的调度机制,是一种大锅饭机制,具体可以看我的blogs上关于scala vs erlang的内容等。 
12.
Caoyuan 
July 12th, 2009 at 13:57 | #12 
Reply | Quote 
在加一点忠告:如果你不是在开发一个大量的、基于短小的信息交换的应用,那么是时候看看Scala了。在我的那篇blog中: 
http://blogtrader.net/dcaoyuan/entry/thinking_in_the_scala_vs 
有些结论我没有明说(因为我的测试数据还没有完全准备好),但如果仔细想,就会明白为什么我已经转向Scala了。而且,我对Scala的Actor感觉良好。 
13.
sw2wolf 
July 12th, 2009 at 19:32 | #13 
Reply | Quote 
haskell的类型系统很好啊! 如果用HASKELL实现类似ERLANG的异步消息系统, 就可以弥补ERLANG动态类型的不足啊? 
14.
Jeffrey Zhao 
July 12th, 2009 at 23:34 | #14 
Reply | Quote 
@jackyz 
引用:外部的表现发生了改变(业务表述都变化了)。这时其实是一个接口的变更,这时,所有调用 open_file 的地方都需要跟着改,这无法避免,无论你在 Java 里也好 .NET 里也好,都是一样的 Erlang 也没什么不同。 
========================= 
的确,任何语言、平台都无法避免接口改变。但是在Java,C#等语言中,可以通过静态检查来进行约束,而Erlang这样的语言却无法做到。这才是我想表达的意思。我并不是说,Erlang会改变接口,而其他语言不会改变。 
15.
Jeffrey Zhao 
July 12th, 2009 at 23:40 | #15 
Reply | Quote 
sw2wolf : 
haskell的类型系统很好啊! 如果用HASKELL实现类似ERLANG的异步消息系统, 就可以弥补ERLANG动态类型的不足啊? 
Haskell的类型系统的确很强大,但是这是否适合Actor模型是一个未知数(包括C# Actor,Scala Actor都有一些问题),因为Haskell毕竟是一个强类型静态检查的语言。Erlang的动态类型特性可以方便的进行开发。不过这种动态类型的确也 造成维护上的缺陷,就如同上面Caoyuan所说,对于业务复杂的系统来说,修改和重构是难以避免的,几乎无法避免需要更强大的类型系统。 
16.
dsun 
July 13th, 2009 at 08:56 | #16 
Reply | Quote 
》Erlang是动态类型的,加上一点点的运行时类型检查和分发(通过is_float, 》 
》is_record等类型检查函数和内生的atom, tuple, List等少量类型)。 
》Erlang的这一特点决定了Erlang不适用于大型的、具有复杂业务逻辑的企业业务系 
》统。 
Erlang具有强大的元编程能力的,甚至不次于Lisp,不用来编写企业应用,是因为其最初要解决的问题非常明确,就是超级复杂的电信级系统要求,开发企业应用就是杀鸡用牛刀,Joe的论文应该仔细看上n遍。 
》协议、函数的接口在这种情况下时需要经常重构的,也意味着需要被改变,这时就 
》会非常非常头疼。 
呵呵,第一次听说动态类型语言比静态类型语言难以重构,Ruby/Python要比Java、C++好重构一个数量级以上。再说了,系统部署之后的动态升级是更为复杂的问题,除了Erlang/OTP,还真没有那个语言、平台系统地解决过这个问题。 
Philip Walder早就为Erlang开发过一个外挂的类型系统,不过效果不是很好,从工程实践上来看,目前的Erlang还不适合于静态类型系统。 
》对于计算密集的任务,Erlang现在还有另一个致命的弱点,就是它的processes的 
》调度机制,是一种大锅饭机制。 
Erlang是不适合计算密集型任务,它的设计目标本就不在此。但是这和Erlang的process调度机制没啥关系。Erlang的调度的设计目标就是为了达成控制上的软实时,不允许有process占用过长的CPU。 
Erlang已经经过数十年的实践检验,且有世界上最复杂的商用系统作为例证。Scala是啥时出来的? 
17.
dsun 
July 13th, 2009 at 08:59 | #17 
Reply | Quote 
当然,如果不是做真正复杂的系统,其实用啥语言都一样。试想,一个用C++开发了7年都没能做出来的系统,用Erlang三年就出了第一个版本,这是个啥概念? 
18.
Caoyuan 
July 13th, 2009 at 09:53 | #18 
Reply | Quote 
》Erlang具有强大的元编程能力的,甚至不次于Lisp,不用来编写企业应用, 
》是因为其最初要解决的问题非常明确, 
》就是超级复杂的电信级系统要求,开发企业应用就是杀鸡用牛刀, 
》Joe的论文应该仔细看上n遍。 
不懂。你的意思是:这个牛刀是不能用来开发企业应用呢?还是牛刀可以但不必? 
》呵呵,第一次听说动态类型语言比静态类型语言难以重构,Ruby/Python要比Java、C++好重构一个数量级以上。 
这个我倒是第一次听到。 
》但是这和Erlang的process调度机制没啥关系。Erlang的调度的设计目标就是为了达成控制上的软实时, 
》不允许有process占用过长的CPU。 
所有我说它适合处理短信息交换。处理大的信息块是需要CPU的。 
19.
dsun 
July 13th, 2009 at 10:08 | #19 
Reply | Quote 
》不懂。你的意思是:这个牛刀是不能用来开发企业应用呢?还是牛刀可以但不必? 
当然能,ThoughtWorks已经有人试图在用Erlang开发企业级应用了。试图让人改变已有的思路,或者放弃已经有的东西是很困难的。不过Erlang针对的主要领域还是极度复杂的电信系统设计。 
》这个我倒是第一次听到。 
可以看看这篇http://www.artima.com/weblogs/viewpost.jsp?thread=4639 
》所有我说它适合处理短信息交换。处理大的信息块是需要CPU的。 
你的意思是Python之类的语言适合计算密集型的任务了?如果真是这样,Erlang又没有强制你一定得创建成千上万的进程,搞一个进程做计算不就行了吗? 
20.
dsun 
July 13th, 2009 at 10:24 | #20 
Reply | Quote 
根据印象,google了一下: 
Domain Specific Languages in Erlang 
http://www.infoq.com/articles/erlang-dsl 
http://qconsf.com/sf2008/file?path=/qcon-sanfran-2008/slides//DennisByrne_DSLs_in_Erlang.pdf 
Conclusion 
I’d like to reiterate my original point about Erlang. This is a fantastic workbench for DSLs. Anonymous functions, regular expression support and pattern matching are only the beginning. Erlang also gives us programmatic access to the tokenized, parsed and abstract forms of an expression. Observe Debasish Ghosh’s string lambdas in Erlang as another example. I hope this article has helped some of you get out of your comfort zones with a new syntax and programming paradigm. I also hope people with think twice before labeling Erlang a specialist language. 
21.
Caoyuan 
July 13th, 2009 at 10:38 | #21 
Reply | Quote 
》Erlang又没有强制你一定得创建成千上万的进程,搞一个进程做计算不就行了吗? 
请再看: 
http://blogtrader.net/dcaoyuan/entry/a_case_study_of_scalable 
http://blogtrader.net/dcaoyuan/entry/async_or_sync_log_in 
22.
dsun 
July 13th, 2009 at 10:45 | #22 
Reply | Quote 
根据印象,google了一下: 
Domain Specific Languages in Erlang 
http://www.infoq.com/articles/erlang-dsl 
qconsf.com/sf2008/file?path=/qcon-sanfran-2008/slides//DennisByrne_DSLs_in_Erlang.pdf
23.
simohayha 
July 13th, 2009 at 10:47 | #23 
Reply | Quote 
@Jeffrey Zhao 
关于类型系统你可以看下这篇文章: 
http://www.reddit.com/r/programming/comments/63tnv/bruce_eckel_33104_im_over_it_java/c02qx55 
erlang是dynamic, strong, latent, structural typing。 
c才是弱类型。 
24.
dsun 
July 13th, 2009 at 11:15 | #24 
Reply | Quote 
看了这个 http://blogtrader.net/dcaoyuan/entry/thinking_in_the_scala_vs,觉得里面的比较过于简单,说明不了啥问题。比如:Process, Actor那个小节中,可曾考虑过和Erlang中selective receive对比?可曾考虑过真实场景中的超级复杂的状态控制如何做? 
可以看看这个: 
Structured Network Programming 
Ulf Wiger 
http://www.erlang.se/euc/05/1500Wiger.ppt 
试着把里面的例子用Scala实现出来看看 
25.
dsun 
July 13th, 2009 at 11:16 | #25 
Reply | Quote 
看 了这个 blogtrader.net/dcaoyuan/entry/thinking_in_the_scala_vs。觉得里面的比较过于简单,说明不了啥 问题。比如:Process, Actor那个小节中,可曾考虑过和Erlang中selective receive对比?可曾考虑过真实场景中的超级复杂的状态控制如何做? 
可以看看这个: 
Structured Network Programming 
Ulf Wiger 
http://www.erlang.se/euc/05/1500Wiger.ppt 
试着把里面的例子用Scala实现出来看看 
26.
fsong 
July 13th, 2009 at 11:46 | #26 
Reply | Quote 
此帖及跟帖都太精彩了,erlang大拿们都冒出来了 
27.
Caoyuan 
July 13th, 2009 at 12:04 | #27 
Reply | Quote 
@dsun 
》Erlang又没有强制你一定得创建成千上万的进程,搞一个进程做计算不就行了吗?请再看: 
http://blogtrader.net/dcaoyuan/entry/a_case_study_of_scalable 
http://blogtrader.net/dcaoyuan/entry/async_or_sync_log_in 
28.
Jeffrey Zhao 
July 13th, 2009 at 12:13 | #28 
Reply | Quote 
simohayha : 
@Jeffrey Zhao 
关于类型系统你可以看下这篇文章: 
http://www.reddit.com/r/programming/comments/63tnv/bruce_eckel_33104_im_over_it_java/c02qx55 
erlang是dynamic, strong, latent, structural typing。 
c才是弱类型。 
多谢改正,的确应该说是“动态类型”。我总结了一下,对于我提出的“Erlang缺少静态检查”主要有两种回应: 
1、接口改变的情况比较少,而且Erlang系统还会涉及到大规模的平滑升级,因此静态检查的要求就降低了。 
2、Erlang在容错方面的优势,使得即使出错,也很容易发现和改正。 
不知道总结地是否正确。 
29.
Caoyuan 
July 13th, 2009 at 12:15 | #29 
Reply | Quote 
@dsun 
》Process, Actor那个小节中,可曾考虑过和Erlang中selective receive对比?可曾考虑过真实场景中的超级复杂的状态控制如何做? 
关键是Process, Actor 的调度机制: 
Erlang中,每个Process得到CPU的机会相等(所以是大锅饭机制),有些重要的Process或许可以通过设优先级来调控,但这种来者不拒的 调度可能导致每个process得到的服务质量都下降。比如,同时有一亿个请求来了,Erlang算了十年(系统没垮,值得表扬)总算算出来了,结果是没 有任何一个人等得了,都跑了。 
Scala中,真正的服务员是有限的,比如100个,即使来了一亿个请求,也同时只处理100个,其余的排队等候,至少先来的100个人能保证服务质量,剩下的如果还有耐心等,会按队列依次处理,等不及的,或者系统timeout了。 
30.
Jeffrey Zhao 
July 13th, 2009 at 12:18 | #30 
Reply | Quote 
dsun : 
看了这个 blogtrader.net/dcaoyuan/entry/thinking_in_the_scala_vs。觉得里面的比较过于简单,说明不了啥 问题。比如:Process, Actor那个小节中,可曾考虑过和Erlang中selective receive对比?可曾考虑过真实场景中的超级复杂的状态控制如何做? 
selective receive的确是不可缺少的,而其他平台上设计的Actor API大都缺乏这方面的支持。我在设计的API也对此有所支持。 
Scala Actor也已经在容错,和Remote Actor方面下手了,相信不久会有所成果可以体现出来。 
31.
Caoyuan 
July 13th, 2009 at 12:24 | #31 
Reply | Quote 
所以在那篇比较中,我给出了选择Erlang或是Scala的一种原则: 
如果单个任务的处理时间(代价)远大于 process/thread的切换时间(代价),选Scala 
如果单个任务的处理时间(代价)约等于或小于 process/thread的切换时间(代价),选Erlang。 
对于大信息块的情况,处理一个信息块的时间通常远大于thread切换时间,选Scala是明智的; 
对于大量的短信息块,比如,ring测试中简单发一个”hello”,thread/process切换的代价可能更大更显著,选Erlang。 
但是,记住,现在JVM的thread切换时间已经是ns级。 
32.
dsun 
July 13th, 2009 at 12:36 | #32 
Reply | Quote 
@Jeffrey Zhao 
关于Erlang的为何采用动态类型可以参看如下两个地方,都有详细的谈及: 
http://www.infoq.com/interviews/Erlang-Joe-Armstrong 
erlang.org/pipermail/erlang-questions/2006-December/024368.html 
@Caoyuan 
Erlang的设计哲学不赞成通过优先级别来影响程序的执行,这个完全应该明确的设计出来,这是一个设计问题。如果依赖于底层的调度优先级,这是设计者在偷懒。 
Erlang只是说可以支撑大规模的并发,但不能因为此,你就不管三七二十一地搞出这么多并发体来,并发粒度的设计还是得根据问题以及应用场景来定。 
33.
Caoyuan 
July 13th, 2009 at 12:37 | #33 
Reply | Quote 
@dsun 
》Joe的论文应该仔细看上n遍。 
Joe的论文我只看了两遍,在我看来,Joe论文的精髓是: 
1、并行的任务应该尽量sharing nothing 
2、异步消息传递是并行任务间最合适的协作方式。 
其余的,包括容错性都可以从这两条中推出来。 
34.
dsun 
July 13th, 2009 at 12:43 | #34 
Reply | Quote 
》如果单个任务的处理时间(代价)远大于 process/thread的切换时间(代价),选 
》Scala 
》如果单个任务的处理时间(代价)约等于或小于 process/thread的切换时间(代 
》价),选Erlang。 
这个我不赞成,如果单个任务的处理时间(代价)约等于或小于 process/thread的切换时间,那么process/thread来进行处理就是个错误的设计,不管用啥语言。 
“小消息,大计算”对所有想通过并发提升性能的系统来说都是适用的,和语言无关。 
35.
Caoyuan 
July 13th, 2009 at 12:47 | #35 
Reply | Quote 
@dsun 
》Erlang的设计哲学不赞成通过优先级别来影响程序的执行, 
》这个完全应该明确的设计出来,这是一个设计问题。 
》如果依赖于底层的调度优先级,这是设计者在偷懒。 
这是Erlang能适用的应用场景,但世界上的应用场景是复杂多样的,很多时候我就需要简单地搞定优先级问题,比如在我的另一篇blog中谈到的 Erlang中log进程是个singleton process时出现的问题(这个问题Ulf也被搞糊涂过,在mail list上提问),还好,我算是比较快定位了问题所在,要不然就狠很地影响到我们的系统的交付了。 
》Erlang只是说可以支撑大规模的并发,但不能因为此,你就不管三七二十一地搞出这么多并发体来, 
》并发粒度的设计还是得根据问题以及应用场景来定。 
“并发粒度的设计还是得根据问题以及应用场景来定”,开始复杂了吧。在这些场景下Erlang的简单性既然没了,我当然选其它的方案。 
36.
Caoyuan 
July 13th, 2009 at 12:50 | #36 
Reply | Quote 
@dsun 
》这个我不赞成,如果单个任务的处理时间(代价)约等于或小于 process/thread的切换时间, 
》那么process/thread来进行处理就是个错误的设计,不管用啥语言。 
Erlang就是这么处理的,而且在这一点上正是它的强项呀? 
“小消息,大计算”:这是指什么? 
37.
jackyz 
July 13th, 2009 at 12:52 | #37 
Reply | Quote 
很热烈啊,有争议是好事情,哈哈,让我们继续探讨。 
@Jeffrey Zhao 
的确,任何语言、平台都无法避免接口改变。但是在 Java,C# 等语言中,可以通过静态检查来进行约束,而 Erlang 这样的语言却无法做到。这才是我想表达的意思。我并不是说 Erlang 会改变接口,而其他语言不会改变。 
首先,我们意识到 Erlang 语言在语法层面并未对“以协议表达接口”进行限制,这是语言的特性,算不上是缺点。然而在工程上,这确实是过于灵活(以至于容易被滥用/引入混乱)的地 方。因此,作为最佳实践,才有了“以函数来表达接口,而不是协议”的这么一条。实际上,我们看 OTP 本身的接口设计,它暴露给用户的几乎所有功能接口都是以函数的形式来进行表达的。作为 OTP 的外部使用者,至少我个人目前还没有碰到过需要以某种“协议”的形式去调用某个功能的地方。 
“以函数来表达接口,而不是协议”,这是我们往下进一步讨论问题的基础。因为,脱离了这一基础,只会得出“过度灵活的消息会导致接口缺乏约束”的结论。然而,对此大家其实并未存在分歧。 
此时,我们会发现,被排除在讨论之外的只是“消息”这个“干扰”因素(因为其他的工业语言没有在语言级别支持这一基础设施)。这个时候再看,在“接 口表达方式”上,Erlang 与 Java、 C# 这类静态类型语言实际已经没有太多差异——大家都是要用函数来表达业务逻辑,以函数名称、参数数目和位置(以及对应的类型)这些要素来形成一套描述基本业 务逻辑的“DSL”,然后再用这套“DSL”组合出具体的业务逻辑。 
我们谈到描述基本业务逻辑的“DSL”,这里面其实只有这么三个要素:1,函数的名称,2,参数数目,3,参数位置以及对应的类型。这在后面的讨论 中我们还会再次涉及。如果说 Erlang 与 Java、 C# 在这个问题上有什么不一样的地方,那就是 Erlang 是动态类型语言,而 Java、 C# 这类语言是静态类型语言。这导致了一些差异。 
@Caoyuan 
Erlang 是动态类型的,加上一点点的运行时类型检查和分发(通过is_float, is_record等类型检查函数和内生的 atom, tuple, List 等少量类型)。 Erlang 的这一特点决定了 Erlang 不适用于大型的、具有复杂业务逻辑的企业业务系统。协议、函数的接口在这种情况下时需要经常重构,也意味着需要被改变,这时就会非常非常头疼。 
将 Caoyuan 兄的观点,按照上述三个要素,以另外一个形式表述,那就是:对于某一个函数而言,Java,C# 这些静态类型语言会在编译和运行时检查函数的名称,参数个数,并根据参数的位置和类型进行检查,判断是否与声明相符。此之为“适合企业业务的严格约束”。而 Erlang 作为动态类型语言,只会在运行时检查函数名称和参数个数(并没有根据位置检查其类型是否与声明相符合的部分)。此之为“缺乏约束,不适合企业业务”。 
然而我却得出了与 Caoyuan 兄不同的结论。前面提到了三个要素:函数的名称,参数数目,参数位置以及对应的类型。Erlang 和 Java,C# 在处理上的主要区别在于:1,参数位置以及对应的类型 Erlang 缺乏检查机制。2,编译时 Erlang 不检查函数的名称和参数的数目(这里主要是指在模块之间的调用不做检查,模块内部的调用是做了检查的)。 
首先说“参数位置以及对应的类型”,与 Java 和 C# 不同的是,这在 Erlang 里是作为一个语法设施提供给了编程者的(也就是函数的模式匹配)。前面 dsun 也谈到这恰好是从另外一个侧面说明 Erlang 是强类型语言而非弱类型语言的例子。如果说,在其他语言中,这个检查的逻辑是:“判断是否与函数的声明相符”,不符就抛出异常,这是语言提供的唯一行为 (而且被认为是好的严格的约束)。那么在 Erlang (以及其他支持模式匹配的函数语言)中,这个逻辑只是被改变成了:“判断是否与函数的某个子句声明相符”,如果相符则执行之,如果全都不符就抛出异常。当然,这一过程只能是放在运行时里。对于这个语法决策,我个人的感觉是,这是一个不错的交易(以不必要的严格换回了灵活)。 
此时,如果要在 Erlang 中实现如同静态类型语言一样的严格约束,其实很简单——我们只需要在 Guard 语句中严格的约束调用类型就行了,并且保证每个接口函数都只有这么一个子句。那么,当参数类型不符时,就会直接速错(与抛异常相对应)。然而,即使按照这 种方式来编程,以“类型系统”的角度来考量,仍然无法觉得满意,因为 Eralng 毕竟只有少数的几种内置类型,这样一用,立马上就会觉得其类型系统不够强大,表达起来不能自如(但别忘了,函数本身也是一个可用的类型系统)。此乃后话, 我们暂且不表。 
我们再来说“缺少”的“编译时”的检查环节,其实 Erlang 已经提供了 xref (交叉引用)工具,用来对模块之间调用的“函数名称和参数个数”进行检查,我此前在《Erlang 引用检查小脚本》一文中已经有所提及。 
然而,值得注意的是,这个工具并没有被默认包含在“编译阶段”之内,这是一个相当有趣的(而且似乎是不严格和不好的)设计。实际上,我们知道在编译 阶段进行的“函数名称和参数个数”检查,并不能完全覆盖所有的情况。比如,在 Java/.NET 中通过反射动态的调用函数,就无法在编译阶段被很好的检查(自动重构自然也就无从谈起)。在 Erlang 中,除了动态调用,还存在大量对函数变量的使用,使得这种情况更为突出。比如 lists 包中大量用到作为参数的函数,等。既然很多情形都不能在编译阶段被检查出来,那么编译器就不徒劳的来做这件事(显而易见的模块内函数调用的检测则不在此 列),这或许是 Erlang 编译器在处理这个问题时的逻辑。 
这其实是一种设计决策,喜欢与否是见仁见智的。有人会认为这是“不严格的检查,不利于重构”,就会有人会认为是“去掉了不必要的约束,更加有利于项 目展开”(每一个模块都应该是内聚的和低耦合的,应该尽量减少对其他模块的编译依赖。请设想,在接口尚未稳定之前,要如何保证 commit 的代码能够总是“通过编译”呢?)。我觉得,这些都是在项目开展过程之中自然都会碰到的两难问题,没有必要去责怪语言。 
谈到重构,我们前面已经提到,业务需求在变,重构在所难免,任何语言概莫能外。修改接口带来的一大堆脏活累活总得有人去干,自动重构的工具能够降低 这种痛苦,但同样无法避免。比如在 Java/.NET 中,你为接口增加了一个参数,自动重构能自动补上,但,仍然需要为每一处修改其找到值的出处,一连串的代码修改势难避免,而且无法自动完成。在 Erlang 中 xref 则只能帮你找到不符之处,你需要自己从头去改,问题是,这两者之间的区别能有多大? 
------------------------------ 
我并不认为 Erlang 是惟我独尊万夫莫敌,它固有其软肋(比如,密集运算,前端,字符处理,等等等等),明智之人必然会选择合适的技术来解决合适的问题。但 Erlang 在它擅长的领域(主要是客户-服务器模式中的服务端)确实是一个有着自己特点的技术,它有着与众不同的语法特点和与之相适应的思维方式。如果我们处处都以 之前积累的经验来判断,必然都会因为“习惯冲突”而产生疑问(比如,按照面向对象的角度,按照设计模式的角度,按照同步调用的角度,等等),这是再正常没 有的了。但我们不妨先别急着做出结论,先留一个疑问在这里,继续前行。等过了一段时间,我们回头再看,说不定就会发现,那个疑问其实已经在另外一处得到了 解答。 
38.
dsun 
July 13th, 2009 at 12:52 | #38 
Reply | Quote 
》Joe的论文我只看了两遍,在我看来,Joe论文的精髓是: 
》1、并行的任务应该尽量sharing nothing 
》2、异步消息传递是并行任务间最合适的协作方式。 
》其余的,包括容错性都可以从这两条中推出来 
咱们的理解还真不一样,我的理解是Joe论文的精髓就是论文的标题, 软件容错是最根本的,你说的1和2都是从这个里面推出来的,甚至热补丁、伸缩、动态升级都可以从软件容错推出来。 
有兴趣的话,可以看看这个: 
http://www.cs.chalmers.se/Cs/Grundutb/Kurser/ppxt/HT2007/general/languages/armstrong-erlang_history.pdf 
或者看看joe armstrong blog上关于容错的文章。 
39.
dsun 
July 13th, 2009 at 12:59 | #39 
Reply | Quote 
》这是Erlang能适用的应用场景,但世界上的应用场景是复杂多样的,很多时候我就 
》需要简单地搞定优先级问题,比如在我的另一篇blog中谈到的 Erlang中log进程是 
》个singleton process时出现的问题(这个问题Ulf也被搞糊涂过,在mail list上 
》提问),还好,我算是比较快定位了问题所在,要不然就狠很地影响到我们的系统的 
》交付了。 
这并不是Erlang的应用场景,这是个和语言无关的设计问题,只是Erlang更推崇这种设计哲学。您定位出来问题后,就是通过调整优先级来解决的吗?如果是这样,只能说您打了个不怎样的补丁,这种问题完全可以通过好的设计来避免,这是个逻辑问题。 
》“并发粒度的设计还是得根据问题以及应用场景来定”,开始复杂了吧。在这些场 
》景下Erlang的简单性既然没了,我当然选其它的方案。 
呵呵,这个怎么就表明Erlang的简单性没有了。 
》Erlang就是这么处理的,而且在这一点上正是它的强项呀? 
Erlang是怎么处理的? 
》“小消息,大计算”:这是指什么? 
参见 Joe Armstrong的Programming Erlang最后一章。 
40.
ShiningRay 
July 13th, 2009 at 13:11 | #40 
Reply | Quote 
不明真相围观群众正在学习中…… 
41.
Caoyuan 
July 13th, 2009 at 13:11 | #41 
Reply | Quote 
@jackyz 
》这个逻辑只是被改变成了:“判断是否与函数的某个子句声明相符” 
对,推到极限,Joe老头甚至在建议: 
形如: 
string:substring( string:S start:I length:J) 
的句子,应该自动改写成: 
string:substring_start_string_length(S, I, J) 
从最近Erlang语法层次的讨论我们看到了什么?我们看到了,试图在语法层次引入静态类型,包括打算增加is_type作为guard函数。 
》其实 Erlang 已经提供了 xref (交叉引用)工具, 
》用来对模块之间调用的“函数名称和参数个数”进行检查 
这些都是目前试图解决这些问题的工具。新版的Erlybird中,我也加入了交叉引用检查,如果函数名和参数个数不符,会自动标上下划波浪线。但这些都不是彻底的,或者说不是完全可靠的。 
在开发企业级应用时,我非常需要了解我写(或改写)的每一个句子的可靠性,完整可靠的静态类型检查就非常重要了。 
另外,类型本身是成系统的,并且,完善、强大的类型系统可以帮助设计出好的应用,Scala的类型系统自恰且有坚实的理论基础,虽然略显复杂但我很喜欢它的逻辑一致性。 
42.
Caoyuan 
July 13th, 2009 at 13:18 | #42 
Reply | Quote 
@dsun 
》这并不是Erlang的应用场景,这是个和语言无关的设计问题 
请看清楚我的表述:这是Erlang**能**适用的应用场景。 
》》》这个我不赞成,如果单个任务的处理时间(代价)约等于或小于 process/thread的切换时间, 
》》》那么process/thread来进行处理就是个错误的设计,不管用啥语言 
》》Erlang就是这么处理的,而且在这一点上正是它的强项呀? 
》Erlang是怎么处理的? 
Erlang就是用便宜的process来处理短信息的,为什么Erlang的process便宜?因为share nothing, 因为创建和切换的代价小呀。用Erlang开发的电信交换机不就是这么处理的吗? 
43.
jackyz 
July 13th, 2009 at 13:32 | #43 
Reply | Quote 
@Caoyuan 
》这些都是目前试图解决这些问题的工具。新版的Erlybird中,我也加入了交叉引用检查,如果函数名和参数个数不符,会自动标上下划波浪线。但这些都不是彻底的,或者说不是完全可靠的。 
问题是,并不存在完全可靠的设施,你要从“最佳实践”的层面来规范你所使用的接口设施。 
比如,你在 Java 里用反射来做调用,同样无法保证你的编译检查是彻底的。正确的做法是用函数来做接口设施(这样才可以得到检查)。这并不是语言支不支持的问题,而是开发的约定和规范。 
在 Erlang 里面,用来做接口设施的应该是函数,而且,最好一个函数就干一件事,不要有一大堆子句了(除非是接受两种差异不大的数据类型),这是从可读性上做要求(同样,也只有这样才可以得到检查)。这同样是开发的约定和规范。 
》在开发企业级应用时,我非常需要了解我写(或改写)的每一个句子的可靠性,完整可靠的静态类型检查就非常重要了。 
了解每一个句子的可靠性,这件事我认为最终应该靠 unit test 来做保证,而不是类型系统。 
》另外,类型本身是成系统的,并且,完善、强大的类型系统可以帮助设计出好的应用,Scala的类型系统自恰且有坚实的理论基础,虽然略显复杂但我很喜欢它的逻辑一致性。 
这件事就是个人喜好了,我不做评论。 
44.
dsun 
July 13th, 2009 at 13:33 | #44 
Reply | Quote 
》请看清楚我的表述:这是Erlang**能**适用的应用场景。 
我的意思是,通过优先级来影响程序的逻辑,不是个好的方法,和用不用Erlang无关。 
》Erlang就是用便宜的process来处理短信息的,为什么Erlang的process便宜?因为 
》share nothing, 因为创建和切换的代价小呀。用Erlang开发的电信交换机不就是这 
》么处理的吗? 
Erlang的process便宜并不见得就得用来处理短消息。我刚刚做过H248的信令处理的性能测试,系统是用Elang做的,每个 transaction启动一个process,信令的处理时间可不短,基本上处理性能(每秒处理的transaction个数)和CPU的个数成线性关 系。如果是信令处理时间很短,就没这个效果了,根本利用不了多核的处理能力,当然,如果用大规模并发来处理短消息是为了控制流程上的简单,则可以这样用, 如果是为了提升性能,就设计错了 
另外,share nothing是为了解决容错这个根本问题。 
45.
dsun 
July 13th, 2009 at 13:43 | #45 
Reply | Quote 
很多“为什么不这样,为什么不那样”,都是因为没有碰到过真正困难问题的拷问。 
> But I have one more question: except that this would be much of work to 
> “inject” the type system into Erlang, what do you think – are there any 
> real obstacles in the nature and the architecture of Erlang/OTP for some 
> kind of static typing? 
> It seems to me that if it possible we would became a language very 
> different from the “original” Erlang. 
There are some original design decisions that make retrofitting a 
static type system difficult. Two of the big ones are dynamic code 
loading and the message passing, but there are most likely lots 
of small snags where functions would have been designed with 
different semantics (and different type signatures!) if dynamic 
typing weren’t available. This is of course partly the “much work” 
argument again, but the combination of dynamic typing and advanced 
pattern matching is a core characteristic of Erlang, which, for one 
thing, leads people to happily write code that would require a PhD 
on type systems to even compile in e.g. ML or Haskell. Many would 
argue that this is not necessarily a good thing – that static typing 
enforces a type discipline that is ultimately good for you. 
The truth of that possibly varies depending on problem domain. 
Anyway, since we have both Concurrent ML and Concurrent Haskell, 
I don’t see that making Erlang statically typed is a high priority. 
Concurrent ML was designed at roughly the same time as Erlang. 
While Erlang’s main requirement was to support programming of 
robust telecoms systems, a main challenge with Concurrent ML was 
to add concurrency in a way that didn’t break the type system. 
This lead to some fundamentally different design decisions, as 
I understand it. 
BR, 
Ulf W 
46.
Jeffrey Zhao 
July 13th, 2009 at 14:02 | #46 
Reply | Quote 
dsun : 
另外,share nothing是为了解决容错这个根本问题。 
share nothing相对immutable(readonly)的优势是什么呢? 
47.
dsun 
July 13th, 2009 at 14:05 | #47 
Reply | Quote 
@Jeffrey Zhao 
immutable是语言层面的问题,share nothing是架构层面的问题 
48.
Jeffrey Zhao 
July 13th, 2009 at 14:12 | #48 
Reply | Quote 
我不知道回复谁了,就没有引用的说一个东西。 
其实,Actor之间如何调度,与操作系统的thread切换并没有必然联系。并不是说,一个线程在执行了某个Actor的一个任务之后,就一定要进行强 制的上下文切换。它完全可以在执行完毕之后,重新取另外一个Actor(同一个Actor,不同Actor都可以)的Mailbox里的任务来执行。所以 “大量短任务”并不一定代表会引起问题,还要看怎么实现的。 
当然,如果是一个长任务,可能执行的时候已经进行了n次切换了。如果是高级语言实现的Actor模型,应该不会强制进行线程切换。我以前实现过简单的 Actor模型和SEDA,这些都是必须考虑的东西。不过我目前还不清楚Erlang和Scala在这方面是怎么处理的,前者是相对低级的VM实现,后者 则是高级平台上的框架实现。 
49.
Jeffrey Zhao 
July 13th, 2009 at 14:17 | #49 
Reply | Quote 
dsun : 
@Jeffrey Zhao 
immutable是语言层面的问题,share nothing是架构层面的问题 
我不是指这个。在如.NET,Java在实现Actor模型的时候,很多时候都是通过传递引用(地址)的方式来进行消息交换。这么做也就是共享对象的实例,但是这些实例是immutable的,只读的,也不会影响并发。 
而Erlang,据我所知,任何传递都是完全拷贝,我认为这方面会带来额外的开销。这是事实吗?为什么Erlang会设计成这样呢? 
50.
Caoyuan 
July 13th, 2009 at 14:26 | #50 
Reply | Quote 
@jackyz 
》在 Erlang 里面,用来做接口设施的应该是函数,而且,最好一个函数就干一件事, 
》不要有一大堆子句了(除非是接受两种差异不大的数据类型),这是从可读性上做要求 
》(同样,也只有这样才可以得到检查)。这同样是开发的约定和规范。 
在Erlang里约定是这样表达的: 
-spec sub_string(string(), pos_integer(), pos_integer()) -> string(). 
sub_string(String, Start, Stop) -> …….. end. 
在Scala里约定是这样表达的: 
def sub_string(string:String, start:Int, stop:Int) { 
…. 

在Erlang中spec是可选的,当然,也可以写成: 
sub_string(String, Start, Stop) when is_list(String), is_integer(Start), is_integer(Stop) -> … end. 
这时主要是用来在运行时检查/分发 
这里的问题是: 
1、在Erlang中,guard函数最初的目的是简单的运行时类型检查和分发约定,-spec, -type是试图加上编译时类型检查;两种机制现在没发合并到一块,或者说,相加上这些时有点晚了,因此有人提出应该写成这样: 
sub_string(String::list(), Start::integer(), Stop::integer()) -> … end. 
这样就统一了,当然这样也就跟Scala等一样了。 
2、即便是有这些约定了,表达更复杂的类型时怎么办?现在Erlang定义复杂类型的主要手段是基本类型加List、Tuple化扩展。但是,这未必是够 的。有人可能认为这样就够了,那也没关系,至少,我处理的问题域中,Scala的完整、一致、包括协变的类型体系很有帮助,它可以在很大程度上帮我分析、 设计整个系统。 
》了解每一个句子的可靠性,这件事我认为最终应该靠 unit test 来做保证, 
》而不是类型系统。 
unit test,类型系统,各司其职,有什么不好吗? 
》如果是信令处理时间很短,就没这个效果了,根本利用不了多核的处理能力, 
》当然,如果用大规模并发来处理短消息是为了控制流程上的简单, 
》则可以这样用,如果是为了提升性能,就设计错了 
process/actor模型本身就是为了让设计和流程控制简单,提升性能倒是有各种各样的办法。好的设计要兼顾简单和性能,actor/process模型对于并发/并行就是一种这样的好的设计。怎么反倒成了设计错了。 
1.
dsun 
July 13th, 2009 at 14:27 | #1 
Reply | Quote 
@Jeffrey Zhao 
Erlang里面也是可以传递引用的,比如:对于大的binary,并且你也可以使用ETS之类的东西避免拷贝;但是会在一定程度上违反Erlang分布式透明的哲学,跨机器时不是还得拷贝。
另外,是不是有性能问题,得测试说了算,不能猜,Erlang在消息传递方面是做了很多优化的。 
再次,最根本的问题还是软件容错,你一个actor传个引用给另外一个actor,然后你这个actor死了,这时会出现啥状况。 
只要大家都静下心来,好好想想如果让你做一个软件容错的系统,你会怎么考虑,就会更好的理解Erlang的设计。 
2.
dsun 
July 13th, 2009 at 14:28 | #2 
Reply | Quote 
说实话,比起解决那些真正复杂的问题,语言的语法真不算个啥。当然好的语法用起来还是很爽的。 
3.
dsun 
July 13th, 2009 at 14:30 | #3 
Reply | Quote 
》process/actor模型本身就是为了让设计和流程控制简单, 
这个只是一个方面 
》提升性能倒是有各种各样的办法。 
process/actor模型可以用作并行计算提升性能。 
》好的设计要兼顾简单和性能,actor/process模型对于并发/并行就是一种 
》这样的好的设计。怎么反倒成了设计错了。 
呵呵,我是说把process/actor用错了。 
4.
litaocheng 
July 13th, 2009 at 14:36 | #4 
Reply | Quote 
>>Erlang的数据抽象能力实在太弱。如果编写一个略显复杂的应用程序,您会发现程序里充斥着复杂的元组。您可能会>>疲于应对那些拥有7、 8个单元(甚至跟多)的元组.. 
你写c/c++,.net里面不是也有一对的指针,对象,数组,列表么?复杂的结构,逻辑必然不是一眼就能看清。你用什么语言写出btree来,不熟悉也是要折腾半天才能搞明白。复杂的东西有喜欢复杂的人去搞明白。 
至于erlang的代码之精简,相信各位就不要在争辩了。 
>>关于erlang中的类型 
erlang是动态语言,强类型。这个灵活当然有代价。 
写出一个好的应用,需要很多精力,需要不断的进行修复完善。 
c/c++, java应用去除bug可能是这样: 
编译期 50% 运行时 50% 
而erlang可能是: 
编译期 30% 运行时 70% 
但是一个好的应用,总是要投入100%的精力,至于什么样的方式不重要。 
通过erlang的type, spec(eep-8),通过dialyzer,我们的erlang程序在编译期可以更安全一些。 
千万别抱怨erlang太脆弱,erlang不是超级强大,但是至少不必你玩过的其他语言脆弱。 
>> 任务调度(process, thread..)机制 
erlang中的调度器,更加接近os级别,其追求process的机会均等。 
某些语言中,你可以保证某个process具有超强的优先级,可以获取更多的cpu。 
在erlang中,这个也不是缺点,为了完成你的需要,可以有多种解决方案: 
1,为某些资源占用密集的process,分配多个process实例,以多达到这个目的(有点蠢) 
2,调整某个process的优先级(erlang中的节点间connect process就是设置priority为max,保证连接能够快速建立),还是不行? 
3,启动一个独立的erlang node,你自己去独享一个vm,想怎么玩你就怎么玩. 
最后,还是尽量让你的应用处于一个平衡的状态,不要让某个process做太多的事情。把80%的任务交给某几个进程去做,肯定不是好主意。 
erlang系统重构的代价,就我自己的经历感觉微不足道,轻而易举。 
erlang还在不断的优化完善,不断引入新的特性,目的不是要成为一个全能冠军,只是在自己擅长的路上做得更好! 
另外顶下dsun! 
5.
Caoyuan 
July 13th, 2009 at 14:43 | #5 
Reply | Quote 
Jeffrey Zhao : 
我不知道回复谁了,就没有引用的说一个东西。 
其实,Actor之间如何调度,与操作系统的thread切换并没有必然联系。并不是说,一个线程在执行了某个Actor的一个任务之后,就一定要进行强 制的上下文切换。它完全可以在执行完毕之后,重新取另外一个Actor(同一个Actor,不同Actor都可以)的Mailbox里的任务来执行。所以 “大量短任务”并不一定代表会引起问题,还要看怎么实现的。 
当然,如果是一个长任务,可能执行的时候已经进行了n次切换了。如果是高级语言实现的Actor模型,应该不会强制进行线程切换。我以前实现过简单的 Actor模型和SEDA,这些都是必须考虑的东西。不过我目前还不清楚Erlang和Scala在这方面是怎么处理的,前者是相对低级的VM实现,后者 则是高级平台上的框架实现。 
@Jeffrey Zhao 
Scala 的Actor可以看作一个轻量的数据结构(在这一点上与Erlang类似),Scala会根据系统资源和任务处理时间自动地管理一个JVM的thread pool,然后把队列或者mailbox中的Actor代理给thread pool中的空闲thread实际执行,执行完毕这个被征用的thread可以还给pool并让下一个actor使用。 
问题是,当正在工作的threads的数量超过CPU的数量时,就一定会导致threads的上下文切换,因为它们通常都需要分配一定的运行机会。 
与Erlang不同的地方是,这个机会的分配,在Scala中,仅在有限数量的工作threads (workers)之间(非所有的actors时间)展开;而Erlang,则在所有的processes(actors)之间展开。 
6.
litaocheng 
July 13th, 2009 at 14:44 | #6 
Reply | Quote 
我不是指这个。在如.NET,Java在实现Actor模型的时候,很多时候都是通过传递引用(地址)的方式来进行消息交换。这么做也就是共享对象的实例,但是这些实例是immutable的,只读的,也不会影响并发。 
而Erlang,据我所知,任何传递都是完全拷贝,我认为这方面会带来额外的开销。这是事实吗?为什么Erlang会设计成这样呢? 
erlang在底层做了很多我们想想不到的优化. 
binary小于64byte是在ower process保存的,这个在作为message发送时,需要copy。大于64byte的binary就是通过reference来进行了。 
至于其他的情况,erlang的老大们很聪明,也很执着。他们知道什么时候使用copy,什么时候仅仅需要一个地址。 
7.
Caoyuan 
July 13th, 2009 at 15:04 | #7 
Reply | Quote 
刚才最后一段话,表达地完整一点: 
与Erlang不同的地方是,这个机会的分配,在Scala中,仅在有限数量的工作threads (workers)之间(非所有的actors之间)展开;而Erlang,则在所有的processes(actors)之间展开。 
另一方面,Scala的上下文切换发生在JVM的thread级,Erlang的上下文切换,发生在轻量的process级,Erlang vm的process切换效率比JVM的thread要好些,这也是为什么我们看到thread-ring的测试中: 
http://shootout.alioth.debian.org/gp4/benchmark.php?test=threadring&lang=all 
Erlang的成绩最好的原因。 
但要注意到,这个成绩的取得,和这个任务本身只是作一个简单的消息传递(!)是相关的,因为简单地传递一个短消息的时间与任务切换的时间是一个数量 级的。但如果让每个ring传完信息后再计算一个很大的Fibbonacci数,那Erlang肯定输给Scala,因为这时主要的时间消耗在计算Fib 数上,任务切换的代价这时反倒不显著了,这当然,也是因为Erlang vm的计算性能比JVM差。 
8.
Trustno1 
July 13th, 2009 at 15:11 | #8 
Reply | Quote 
对于Distribution的系统来说,加入Static Typing 最为麻烦的事情是如何传输类型如何进行类型的推导. 
T={people,Age=1,Score=2,Id=3}. 
P!T. 
revieve 
{people,int:Age,int:Score,int:Id}-> 
….. 
那么势必要求,T在传输的时候是要这样吗?{people,(Int)1,(Int)2,(Int)3}. 
如果某个filed是个非常复杂的继承结构呢? 
如果我传输的是一个List串呢?每个Item都要标示类型码?(如果List的类型系统可以容纳不同的Item类型)。 
Ok.这些东西都解决了.那么Recvieve以后我们如何分派类型呢?要知道Static Type的类型是编译期生成的.而recieve的数据是运行时得到结果的. 
Recieve 
{people,int:Age,int:Score,int:Id}-> 
在编译完成后就无法更改了.按照上面某楼说的,如果协议变了,变成了 
{people,int:Age,float:Score,int:Id}.那么接收方该怎么办呢?类型推导吗? 
如果你想类型推导,那么问题就来了. 
如果我们写成这样 
Recieve 
{people,var:Age,var:Score,var:Id}-> 
do(Score). 
如果此时var类型是一个新类型.而do(Score)函数在编译期对这种类型完全没有展开过.那么我们在执行do(Score)之前是不是要对var类 型做一次新的编译推导?可以设想一下do(Score)内部使用的类型推导像Boost库里面某些变态代码那么复杂,那么运行时编译一次的代价又有多高 呢? 
9.
Jeffrey Zhao 
July 13th, 2009 at 15:48 | #9 
Reply | Quote 
Caoyuan : 
@Jeffrey Zhao 
Scala的Actor可以看作一个轻量的数据结构(在这一点上与Erlang类似),Scala会根据系统资源和任务处理时间自动地管理一个JVM的 thread pool,然后把队列或者mailbox中的Actor代理给thread pool中的空闲thread实际执行,执行完毕这个被征用的thread可以还给pool并让下一个actor使用。 
问题是,当正在工作的threads的数量超过CPU的数量时,就一定会导致threads的上下文切换,因为它们通常都需要分配一定的运行机会。 
与Erlang不同的地方是,这个机会的分配,在Scala中,仅在有限数量的工作threads (workers)之间(非所有的actors时间)展开;而Erlang,则在所有的processes(actors)之间展开。 
我觉得有两个东西还是需要分清楚的: 
1、操作系统的线程切换 
2、Actor模型的线程切换 
我们说的,Scala中一个Thread执行完一个Actor的消息再去执行另一个Actor的消息,其中发生的是第2种线程切换,而在实现时,它可能只是重新拿出一个Actor来再获取一个消息,再执行,期间根本没有发生第1种切换。 
如果执行一个很大的Fib计算,那么在这里不会发生第2种切换,而第1种切换是必然会发生的。因为第1种切换由操作系统管理,例如分配时间片,计算时间超过时间片了,于是换一个操作系统线程。 
即使是Scala这种使用线程池,有限个Thread,例如100个worker,它执行时也是在处理例如10000个Actor。 
而Erlang,我不知道它的调度方式。据你的描述,它似乎不是由(相对)少数个worker来处理大量Actor,而是像操作系统一样,为所有的process均匀分配计算资源? 
10.
Caoyuan 
July 13th, 2009 at 15:48 | #10 
Reply | Quote 
@Trustno1 
首先,静态类型的传递,再Scala的Actor模型中,pattern match除了可以match List,Array,Tuple,也可以直接针对类型,特别地case class: 
case class People(age:Int, score:Number, id:Int, a:Array[Any]) 
react { 
case People(age, score:Int, id:Int, _) => do(score) 
case People(age, score:Float, id:Int, _) => do(score) 
case People(age, score:Number, id:Int, Array(a1:Int, a2:String, _)) => do(score) 
case _ => 

其次,如果接口变化了,源码修改了,那么得先静态编译一遍,调用者和被调用者的代码都得通过静态类型检查,然后就是类型安全的代码了。至于怎么动态reload到运行环境中,如果系统中没有共享的状态,简单些,否则,设计迁移方案。 
11.
litaocheng 
July 13th, 2009 at 15:49 | #11 
Reply | Quote 
@Trustno1 这视角,独了~赞! 
如果加入静态类型: 
P ! {people,int:Age,int:Score,int:Id} % version 1 
P ! {people,int:Age,float:Score,int:Id} % version 2 
receive 
{people,int:Age,int:Score,int:Id}-> % version 1 
do(Score) 
…. 
{people,int:Age,float:Score,int:Id}-> % verson 2 
… 
do(Score) 
end 
do(int:Score) -> 
…; 
do(float:Score) -> 
…. 
Erlang的风格: 
P ! {people,Age,Score,Id} % Score is integer or float, version 1 = version 2 
receive 
{people,Age,Score,Id} -> 
do(Score) 
end 
do(Score) when is_integer(Score) -> 
…; 
do(Score) when is_float(Score) -> 
…. 
Erlang的方法有什么不好么? 
Erlang內建分布式支持,Erlang的哲学是非防御性编程,有错误就抛出来。所以静态类型不会给erlang带来太多的好处,相反是复杂罗嗦的代码。 
静态类型所做的工作,不会100%保证你的代码质量,你还是需要test. erlang知道这个道理,让代码简介,让开发速度加快,就成了目标。 
12.
jackyz 
July 13th, 2009 at 15:58 | #12 
Reply | Quote 
@Caoyuan 
》1、在Erlang中,guard函数最初的目的是简单的运行时类型检查和分发约定,-spec, -type是试图加上编译时类型检查;两种机制现在没发合并到一块,或者说,相加上这些时有点晚了,因此有人提出应该写成这 样:sub_string(String::list(), Start::integer(), Stop::integer()) -> … end. 
这样就统一了,当然这样也就跟Scala等一样了。 
你说的“两种机制现在没法合并到一块”,我觉得这倒还真是一个问题。 
现在 -spec 方式的一个严重问题是,同样的信息你必须要表达两次,一次是在写代码的时候来表达,另外一次是在写 -spec 的时候表达。而且,在这个两次表达之中,还凭空多出了一个新的来源。那么,如果这两者发生了冲突,应该怎么办?又比如说,如果我更新了程序代码,却还没有 更新 -spec 的内容,岂不是会出现 spec 与 code 的不一致,又要由谁通过什么机制来维护这两者的一致呢?如果这必须要依靠人的自觉性,那和编程的约定和规范又有多大的分别? 
我觉得,在这里应该遵循“单一信息来源”的原则。因为 Erlang 已经将“参数的位置及其对应类型”(实际上还包含了对应的值)作为函数模式匹配的语法设施提供给了编程者,让其在运行时中使用,那么编程者的程序本身就已 经完整的包含了“参数位置及其对应类型”的全部信息,而且是对这一信息最为精确的也是最终的描述(再邪乎那么一点就有“程序即文档”的意思)。也就是说, 现在需要由我们手工来添加的 -spec 信息如果能从程序逻辑之中“抽取”出来,就会是最为精确的而且能够保证同步。 
但现在的状况是,你不仅需要手工的方式来重建这些信息,而且,其表达能力还足够强大,简直就是另外一个语言,以至于可以从中发展出一套复杂的类型系统。然而这套类型系统却又还不是编译过程中的必须,如此折腾为哪般? 
在我自己的实践中,目前仍然以 @spec (R12的文档注释)语法为主。我就是将其当作外挂的注释规范和文档生成——一个不是编译阶段所必须的类型系统,注定会是一个外挂的半吊子类型系统,我对它没有太高的期望。 
》2、即便是有这些约定了,表达更复杂的类型时怎么办?现在Erlang定义复杂类型的主要手段是基本类型加List、Tuple化扩展。但是,这 未必是够的。有人可能认为这样就够了,那也没关系,至少,我处理的问题域中,Scala的完整、一致、包括协变的类型体系很有帮助,它可以在很大程度上帮 我分析、设计整个系统。 
谈到参数的复杂类型,显然,标准的做法就是在 .hrl 文件中定义公用的 record 以作为多个模块之间的共享。但是我现在对于这么做的必要性开始有一点点疑惑——我们真的需要那么复杂的参数类型么? 
尤其是在表达接口的时候。在这个层次,我个人更加关注的是,这个参数表达的是什么(貌似名称更重要),而不是它是怎么构成的。在我的编程实践中,很 少碰到需要像 C 一样“需要定义一个复杂的 Struct 来作为输入输出的约定”的情况,大部分的情况下 tuple 和 list 都已经足够。当然,这是我个人的编程实践,也许并不具有普遍性。或许只有拿个具有代表性的例子出来才有讨论的基础。 
》unit test,类型系统,各司其职,有什么不好吗? 
如果不将复杂的业务逻辑通过几乎同样复杂的类型系统来进行表述(比如,回退到 Erlang 的几种基本类型),那么这样的类型检查系统还有那么大的必要么? 
申明:以上回帖,个人观点居多,小心取舍。 
13.
Caoyuan 
July 13th, 2009 at 16:00 | #13 
Reply | Quote 
》我们说的,Scala中一个Thread执行完一个Actor的消息再去执行另一个Actor的消息, 
》其中发生的是第2种线程切换,而在实现时,它可能只是重新拿出一个Actor来再获 
》取一个消息,再执行,期间根本没有发生第1种切换 
可以这么理解,但在执行一个Actor的任务**期间**有可能发生第1种切换。 
》如果执行一个很大的Fib计算,那么在这里不会发生第2种切换, 
》而第1种切换是必然会发生的。 
也可以这么理解,但如果处在“工作”状态的actors们此时正好有计算完成的,会发生第2种切换:新的actor被代理给这个空出来的thread。 
》即使是Scala这种使用线程池,有限个Thread,例如100个worker, 
》它执行时也是在处理例如10000个Actor。 
那一个简化的情况作例子,假设这100个workers总是能在同样的时间里完成任务,那么你会看到输出/打印的结果是:计算头100个,需要时间,输出100个的结果,然后下100个…,每100个一批。 
同样的假设下,Erlang则可能是:10000个process在同时计算,等了很久很久,然后看到10000个结果同时出来了。 
14.
Jeffrey Zhao 
July 13th, 2009 at 16:12 | #14 
Reply | Quote 
@Caoyuan 
经过你的解释,我感觉我的确理解了Erlang和Scala在调度上的区别了,呵呵。在某些情况下的确是Scala较为有优势(我以前的C# Actor模型也是类似方式)。 
不过话说回来,Erlang的调度方式,需要能够中断任务,保存上下文,才能把计算能力分配给其他任务。而使用Java或.NET这种高级平台,进一步抽象出来的Actor模型,基本上也只能选择Scala这种方式了。 
15.
zoomquiet 
July 13th, 2009 at 16:14 | #15 
Reply | Quote 
呃讨论的本体都有点歪,不是 弱类型是否足够用,也不是在erl中如何对付复杂业务最好; 
应该是 “erl 的最大问题” 本身: 
- 问题成立嘛? 
- 真是是最大的问题? 
咔咔咔,就俺看,这种讨论已经被Erlang 开发的各种大型商用软件长年运行的现实反驳了, 
是否最佳实现? 
用OTP 就保证了开发的规范和运行的稳健了, 
具体内在机制是否合理… 
只能说,没有完美的语言,在对口的领域,可以解决大多数问题,就是好语言 
16.
dsun 
July 13th, 2009 at 16:16 | #16 
Reply | Quote 
》同样的假设下,Erlang则可能是:10000个process在同时计算,等了很久很久,然 
》后看到10000个结果同时出来了。 
这是Erlang特定设计的结果,不像其他平台完全没有规章。如果“计算头100个,需要时间,输出100个的结果,然后下100个…,每100个一批”是你需要的功能,那为何不通过设计来明确规范地表达,而非得依靠底层平台的在某个时间的某种特定实现逻辑呢? 
计算能力的分配是个设计问题。 
17.
Caoyuan 
July 13th, 2009 at 16:28 | #17 
Reply | Quote 
@Jeffrey Zhao 
其实现在各种语言互相借鉴的趋势很明显,ErlangVM说不定哪天也会支持Scala方式的调度,而JVM下的Thread实现也越来越轻量,哪天也能在必要的时候达到Erlang的调度效果。
但在目前而言,除了大量、软实时、短信息交换的应用,Scala适用领域应该更大些。这也是为什么在写了进10万行Erlang代码后,我开始更多 地用Scala,因为我碰到的问题域都是业务逻辑比较复杂的。但Erlang的process模型和function style让我获益多多,用到Scala里也是得心应手。 
18.
Caoyuan 
July 13th, 2009 at 16:33 | #18 
Reply | Quote 
@dsun 
dsun : 
》同样的假设下,Erlang则可能是:10000个process在同时计算,等了很久很久,然 
》后看到10000个结果同时出来了。 
这是Erlang特定设计的结果,不像其他平台完全没有规章。如果“计算头100个,需要时间,输出100个的结果,然后下100个…,每100个一批”是你需要的功能,那为何不通过设计来明确规范地表达,而非得依靠底层平台的在某个时间的某种特定实现逻辑呢? 
计算能力的分配是个设计问题。 
总得先搞清楚不同平台下特定的结果吧,这样在出现某些特定故障的时候才能知所以然。我可能比较笨,直到去年才想清楚这两种调度机制的不同。 
至于设计,Erlang下也能设计出同样的结果(每100个一批),Scala下也能实现10000个同时,但具体实现的复杂程度各不相同。 
19.
Jeffrey Zhao 
July 13th, 2009 at 16:36 | #19 
Reply | Quote 
计算能力的分配是个设计问题。 
例如在传统场景中,thread per request(对应到Erlang便是process per message),这样虽然可以利用所有计算资源,但是随着message数量提高,Response Time增加,吞吐量会下降。而Scala的做法,吞吐量不变,虽然Response Time也增加了。 
这个就是SEDA架构解决的问题。在Erlang来说,如何实现Scala的要求?是不是需要开启有限数量个节点来计算大量任务?如果是较为复杂的做法,我觉得我在这方面还是更倾向于Scala。 
20.
dsun 
July 13th, 2009 at 16:38 | #20 
Reply | Quote 
》其实现在各种语言互相借鉴的趋势很明显,ErlangVM说不定哪天也会支持Scala方 
》式的调度,而JVM下的Thread实现也越来越轻量,哪天也能在必要的时候达到 
》Erlang的调度效果。 
不明设计目标的借鉴,对语言来说就是灾难。Scala的调度方式,目前来说,其实就是为了实现高并发在JVM上打的补丁。 
》但在目前而言,除了大量、软实时、短信息交换的应用,Scala适用领域应该更大 
》些。这也是为什么在写了进10万行Erlang代码后,我开始更多地用 Scala,因为 
》我碰到的问题域都是业务逻辑比较复杂的。但Erlang的process模型和function 
》style让我获益多多,用到Scala里也是得心应手。 
不知道您在解决复杂的业务逻辑时,有没有考虑过软件容错问题。 
21.
dsun 
July 13th, 2009 at 16:41 | #21 
Reply | Quote 
》例如在传统场景中,thread per request(对应到Erlang便是process per 
》message),这样虽然可以利用所有计算资源,但是随着message数量提 
》高,Response Time增加,吞吐量会下降。而Scala的做法,吞吐量不变,虽然 
》Response Time也增加了。 
》这个就是SEDA架构解决的问题。在Erlang来说,如何实现Scala的要求?是不是需要 
》开启有限数量个节点来计算大量任务?如果是较为复杂的做法,我觉得我在这方面还 
》是更倾向于Scala。 
你这个例子举得挺好的,这种场景正是Erlang的强项,取得的效果正好和你描述的相反,自己google吧。 
22.
Caoyuan 
July 13th, 2009 at 16:47 | #22 
Reply | Quote 
Jeffrey Zhao : 
计算能力的分配是个设计问题。 
例如在传统场景中,thread per request(对应到Erlang便是process per message),这样虽然可以利用所有计算资源,但是随着message数量提高,Response Time增加,吞吐量会下降。而Scala的做法,吞吐量不变,虽然Response Time也增加了。 
这个就是SEDA架构解决的问题。在Erlang来说,如何实现Scala的要求?是不是需要开启有限数量个节点来计算大量任务?如果是较为复杂的做法,我觉得我在这方面还是更倾向于Scala。 
@Jeffrey Zhao 
对。 
Scala中,可以设计成per request per actor,然后让Scala去调度需要的实际threads数目。Scala在这方面还有一个值得称道的地方,那就是会根据系统的容量先估计一个需要的 threads数量,然后根据实际运行时任务完成的速度自动调节threads的数量,比如,如果任务很快完成了,而等待的actor又很多,就适当增加 threads的数量,反之,就减少。 
23.
dsun 
July 13th, 2009 at 16:48 | #23 
Reply | Quote 
》总得先搞清楚不同平台下特定的结果吧,这样在出现某些特定故障的时候才能知所以 
》然。 
对应用开发者来说,这个还是不要搞清楚的好,搞清楚了在设计上就会偷懒,然后就会出莫名其妙的故障。 
24.
Caoyuan 
July 13th, 2009 at 16:51 | #24 
Reply | Quote 
@dsun 
》Scala的调度方式,目前来说,其实就是为了实现高并发在JVM上打的补丁。 
我认为不能叫补丁,而应该是非常合理的模式:Service good first. 
》不知道您在解决复杂的业务逻辑时,有没有考虑过软件容错问题。 
当然要。在不同的层次用不同的手段。 
25.
dsun 
July 13th, 2009 at 16:53 | #25 
Reply | Quote 
》当然要。在不同的层次用不同的手段。 
能不能举个实例,简单谈谈? 
26.
Caoyuan 
July 13th, 2009 at 16:54 | #26 
Reply | Quote 
dsun : 
》总得先搞清楚不同平台下特定的结果吧,这样在出现某些特定故障的时候才能知所以 
》然。 
对应用开发者来说,这个还是不要搞清楚的好,搞清楚了在设计上就会偷懒,然后就会出莫名其妙的故障。 
想请教一下什么是设计?是指艺术设计吗? 
27.
Jeffrey Zhao 
July 13th, 2009 at 16:55 | #27 
Reply | Quote 
@Jeffrey Zhao 
对。 
Scala中,可以设计成per request per actor,然后让Scala去调度需要的实际threads数目。Scala在这方面还有一个值得称道的地方,那就是会根据系统的容量先估计一个需要的 threads数量,然后根据实际运行时任务完成的速度自动调节threads的数量,比如,如果任务很快完成了,而等待的actor又很多,就适当增加 threads的数量,反之,就减少。 
这也是SEDA特性了,使用可调节的线程池和控制器进行控制。 
不过dsun兄弟认为我举的这个例子正好是Erlang的强项,希望您可以解答一下。想听听同时有Erlang和Scala背景的程序员的看法。:) 
28.
dsun 
July 13th, 2009 at 16:59 | #28 
Reply | Quote 
》想请教一下什么是设计?是指艺术设计吗? 
呵呵,真会开玩笑。比如基于优先级别来进行计算能力的分配就是在偷懒,@litaocheng 给出的方法就是设计。 
29.
Caoyuan 
July 13th, 2009 at 17:01 | #29 
Reply | Quote 
dsun : 
》当然要。在不同的层次用不同的手段。 
能不能举个实例,简单谈谈? 
@dsun 
在网络层次,比如,股票数据,除了卫星大锅,还有交易所附近的光纤连接的主站,以及分布在各地的各个点到主站的连接; 
在主机层次,主机本身可能需要是双电源的,硬盘是要raid好的; 
在数据库层次,要cluster好的; 
在应用服务器层次,也要cluster好的; 
在笔记本电脑这边,要保证电池充足了电,然后联在电源上,同时还有一部直线电话和手机可以下单。 
30.
dsun 
July 13th, 2009 at 17:11 | #30 
Reply | Quote 
》在网络层次,比如,股票数据,除了卫星大锅,还有交易所附近的光纤连接的主站, 
》以及分布在各地的各个点到主站的连接;。。。。 
您举得是系统容错方面的措施,我说的是软件容错。比如,软件中有个bug,在处理某条消息时,会导致系统崩溃,虽然应用服务器做了集群,因为是同样的软件版本,就可能导致整个集群崩溃,这种情况咋解决。 
31.
Trustno1 
July 13th, 2009 at 17:11 | #31 
Reply | Quote 
其实,我赞成static type对健壮性和开发效率上的优势.但是这只能局限于单机系统.在分布式环境下,静态类型其实帮不了多少忙.比如说上面某楼提出的openfile修改的问题.在分布式环境下,无论是否引入static type或者OO都无法解决问题. 
react { 
case People(age, score:Int, id:Int, _) => do(score) 
case People(age, score:Float, id:Int, _) => do(score) 
case People(age, score:Number, id:Int, Array(a1:Int, a2:String, _)) => do(score) 
case _ => 

像caoyuan给出的这个方案.无法避免全面性的更动. 
这一点上,Haskell社区其实研究了很多年了.对于分布式的类型系统基本上目前没有什么好方法.所以那个以模仿Erlang为目标的Distribution Haskell很早就夭折了. 
32.
zhangyuchi 
July 13th, 2009 at 17:14 | #32 
Reply | Quote 
dsun : 
》例如在传统场景中,thread per request(对应到Erlang便是process per 
》message),这样虽然可以利用所有计算资源,但是随着message数量提 
》高,Response Time增加,吞吐量会下降。而Scala的做法,吞吐量不变,虽然 
》Response Time也增加了。 
》这个就是SEDA架构解决的问题。在Erlang来说,如何实现Scala的要求?是不是需要 
》开启有限数量个节点来计算大量任务?如果是较为复杂的做法,我觉得我在这方面还 
》是更倾向于Scala。 
你这个例子举得挺好的,这种场景正是Erlang的强项,取得的效果正好和你描述的相反,自己google吧。 
dsun的这个结论我也看不明白了,怎么结果正好相反呢?我认为随着请求量的不断增加,erlang和scala都会遇到响应时间变长的情况。只是erlang可能会让所有请求都超时,而scala让一部分请求超时。到了这个时候不管用哪种方案都该扩容了。 
33.
mryufeng 
July 13th, 2009 at 17:16 | #33 
Reply | Quote 
erlang 的系统设计最重要的一点就是公平。IO公平 经常调度公平 bif执行公平一切都是在公平的基础上的。这个公平在我来看, 对于大型的系统来讲至关重要的,就好比我们要构筑现实的公平的社会一样。你想多利用资源,你的靠人数(进程数取胜),等于告诉系统,我的人头多,我的票数 就多,分的资源就多。这是erlang的哲学。 
34.
dsun 
July 13th, 2009 at 17:17 | #34 
Reply | Quote 
》dsun的这个结论我也看不明白了,怎么结果正好相反呢?我认为随着请求量的不断 
》增加,erlang和scala都会遇到响应时间变长的情况。只是erlang可能会让所有请求 
》都超时,而scala让一部分请求超时。到了这个时候不管用哪种方案都该扩容了。 
呵呵,没表述清楚。我的意思是Erlang的优雅降级能力要优于Scala,而且伸缩起来也更容易一些。 
35.
Caoyuan 
July 13th, 2009 at 17:19 | #35 
Reply | Quote 
》erlang中的调度器,更加接近os级别,其追求process的机会均等。 
》某些语言中,你可以保证某个process具有超强的优先级,可以获取更多的cpu。 
》在erlang中,这个也不是缺点,为了完成你的需要,可以有多种解决方案: 
》1,为某些资源占用密集的process,分配多个process实例,以多达到这个目的(有点蠢) 
》2,调整某个process的优先级(erlang中的节点间connect process就是设置priority为max, 
》保证连接能够快速建》立),还是不行? 
》3,启动一个独立的erlang node,你自己去独享一个vm,想怎么玩你就怎么玩. 
》最后,还是尽量让你的应用处于一个平衡的状态,不要让某个process做太多的事情。 
》把80%的任务交给某几个进程去做,肯定不是好主意。 
好吧,我们看看这个设计。 
1、如果某个任务不能分解为多个processes并行算,那么方案1就不行了; 
2、那么给它个优先级,比如加一级,在系统有4个CPU时,我们的测试发现在有其它400个processes时似乎够了。然后,由于业务的需要,甚至,由于CPU增加到了8个,我们的系统中同时运行的processes数现在增加到了800个,这时可能发生什么呢? 
假设这个特别process的优先级导致它能10倍于普通的processes获得CPU的时间,那么在400个其它processes运行时,它的机会是: 
10 / (10 + 400) = 2.44%, 
而在800个其它processes运行时,它的机会是: 
10 / (10 + 800) = 1.23%, 
就是说,系统的CPU数增加了,它的运行机会反倒下降了,这时你会发现数据全堵在它这了,Erlang最后crash掉。 
以上就是我在用erlang中的log函数时遇到的情况,如果不了解Erlang的调度机制,你能找到问题所在么?或者,你能在一开始时就知道方案1和方案2的设计可能不合适吗? 
36.
mryufeng 
July 13th, 2009 at 17:20 | #36 
Reply | Quote 
其 实对于类型方面的事情 我想举的例子是 linux内核 只要2中数据 数组和list 可以对付几百万行的系统的需求。erlang的设计定位是类似电话系统这样的应用,对于这类应用来讲,目前的数据结构和表达方式,个人感觉是够用了。但是 如果把它应用于其他领域,超过了设计者的预期,所以会很不顺,这是事实! 
37.
mryufeng 
July 13th, 2009 at 17:25 | #37 
Reply | Quote 
其 实erlang的优先级没用的。进程的优先级确保了进程能够优先调度,但是记住一个原则就是公平原则。你只是调度,并不说明你可以多获取cpu时间,你的 cpu时间每次还是3000redution. 所以对于cpu密集的应用 唯一的办法就是多开进程实例,相对于其他进程的数量优势来获取计算力的优势。 
38.
dsun 
July 13th, 2009 at 17:27 | #38 
Reply | Quote 
@Caoyuan 
我是说@litaocheng至少在设计上考虑这个问题了,没有偷懒,方案2还是基于优先级的。他不是还有方案3的吗。 
39.
Caoyuan 
July 13th, 2009 at 17:30 | #39 
Reply | Quote 
dsun : 
》在网络层次,比如,股票数据,除了卫星大锅,还有交易所附近的光纤连接的主站, 
》以及分布在各地的各个点到主站的连接;。。。。 
您举得是系统容错方面的措施,我说的是软件容错。比如,软件中有个bug,在处理某条消息时,会导致系统崩溃,虽然应用服务器做了集群,因为是同样的软件版本,就可能导致整个集群崩溃,这种情况咋解决。 
这种情况下,Erlang照样可能垮掉,就算superviser救起来,过一会照样垮。见我blog中log导致erlang crash的例子。 
JVM下,某个actor处理某条消息时出错也同样可以catch住,特别地,如果这个actor也跟其它actors sharing nothing,垮掉就垮掉,其它的actors也可以平安无事。 
40.
mryufeng 
July 13th, 2009 at 17:31 | #40 
Reply | Quote 
@Caoyuan 
R13B以后支持多进程队列的调度,CPU的利用率问题提高了非常多,cpu在调度的时候考虑到负载均衡了, 但是在设计的时候还是要避免单进程设计。 
41.
zhangyuchi 
July 13th, 2009 at 17:32 | #41 
Reply | Quote 
我在实际编写erlang程序的时候感觉到,如果record要是能支持继承关系,那么就能表达更复杂的数据结构,也就能承载更复杂的业务逻辑了。 
42.
mryufeng 
July 13th, 2009 at 17:34 | #42 
Reply | Quote 
@Caoyuan 
erlang的otp设计者意识到这个问题了,你看crypt 应用的设计就不是单进程的设计了。这个logserver的问题最好是在一个独立的节点里面开。 
43.
litaocheng 
July 13th, 2009 at 17:34 | #43 
Reply | Quote 
@Caoyuan 
恩,你说的问题,我也遇到过。 
erlang vm因为某个process累计了上百万的消息而crash。 
这个问题,我们使用1的蠢办法解决,虽然不是很漂亮。 
其实归根结底是因为我们的系统不平衡,这个负载的“热点”区域应该隔离到一个独立的系统中。比如独立的log server。如果再次出现“热点”,那明显是系统需要升级了。 
44.
Caoyuan 
July 13th, 2009 at 17:36 | #44 
Reply | Quote 
dsun : 
@Caoyuan 
我是说@litaocheng至少在设计上考虑这个问题了,没有偷懒,方案2还是基于优先级的。他不是还有方案3的吗。 
先澄清一下,我好像没有说要用优先级来达到特定的目标,我想我说的**即使**可以利用优先级来达到目标,也还是会出错。 
那么说说方案3。这在很多情况下是好的设计,好在比较彻底的解偶。实际上我们的系统中,最后的设计是Erlang快速处理前端请求,Scala的另一台独立的机器上完成大量的XML文本解析和查询任务,两者之间通过JInterface通讯。 
45.
fsong 
July 13th, 2009 at 17:37 | #45 
Reply | Quote 
@dsun 
各位争论得正酣,我问个题外的话,其他大拿的大名我都有所闻,但是恕我孤陋寡闻,dsun大拿有没有博客? 
46.
jackyz 
July 13th, 2009 at 17:42 | #46 
Reply | Quote 
@fsong,建议你先去阅读大拿翻译的《Joe Thesis》 
47.
Caoyuan 
July 13th, 2009 at 17:45 | #47 
Reply | Quote 
至 于前面说的log问题,后来的方案是将异步log调用改为同步处理,即在同一个process中等待log process完成并返回后在继续,这样的好处是每个请求的任务在一个process中同步完成,不需要去计算分解到不同任务的process后平衡的问 题,这样,简单地scale-out(加机器)就可以实现横向扩展。 
48.
Caoyuan 
July 13th, 2009 at 17:54 | #48 
Reply | Quote 
mryufeng : 
@Caoyuan 
erlang的otp设计者意识到这个问题了,你看crypt 应用的设计就不是单进程的设计了。这个logserver的问题最好是在一个独立的节点里面开。 
对,这个很重要。记得第一次跟他们提这个问题时,他们还争辩是我log方法用错了:-) 
49.
fsong 
July 13th, 2009 at 18:00 | #49 
Reply | Quote 
jackyz : 
@fsong,建议你先去阅读大拿翻译的《Joe Thesis》 
确实不知道还有人翻译了,汗.学习. 
50.
dsun 
July 13th, 2009 at 19:24 | #50 
Reply | Quote 
接着讨论,呵呵。 
》这种情况下,Erlang照样可能垮掉,就算superviser救起来,过一会照样垮。见我 
》blog中log导致erlang crash的例子。 
Erlang中处理这个消息的进程是会垮掉,但是系统对于其他消息照样能处理,所以这就是软件容错。 
》JVM下,某个actor处理某条消息时出错也同样可以catch住,特别地,如果这个 
》actor也跟其它actors sharing nothing,垮掉就垮掉,其它的actors也可以平 
》安无事。 
这个就加重应用程序设计者的负担了,而且还不容易做好。这就是工程化和非工程化的区别。 
• dsun 
July 13th, 2009 at 19:26 | #1 
Reply | Quote 
fsong : 
jackyz : 
@fsong,建议你先去阅读大拿翻译的《Joe Thesis》 
确实不知道还有人翻译了,汗.学习. 
这个不是我翻译的。呵呵 
• 
Caoyuan 
July 13th, 2009 at 19:42 | #2 
Reply | Quote 
》》这种情况下,Erlang照样可能垮掉,就算superviser救起来,过一会照样垮。见我 
》》blog中log导致erlang crash的例子。 
》Erlang中处理这个消息的进程是会垮掉,但是系统对于其他消息照样能处理, 
》所以这就是软件容错。 
错,这种情况下Erlang会完全地垮掉,在OS中消失,留下一个大大的dump文件。 
• 
dsun 
July 13th, 2009 at 19:53 | #3 
Reply | Quote 
》以上就是我在用erlang中的log函数时遇到的情况,如果不了解Erlang的调度机制, 
》你能找到问题所在么?或者,你能在一开始时就知道方案1和方案2的设计可能不合 
》适吗? 
我不认为发现这个问题必须得了解Erlang的调度机制。Erlang提供的强劲的系统监控、检测手段发现这个问题应该不会很困难。 
一开始做方案和设计时,实际的检验和验证是少不了的,否则就是瞎猜。所以如果一开做方案时就模拟真实或者仿造极端环境验证,就会避免很多问题。 
• 
dsun 
July 13th, 2009 at 19:56 | #4 
Reply | Quote 
》错,这种情况下Erlang会完全地垮掉,在OS中消失,留下一个大大的dump文件。 
我可没说你那个log问题,我说的是消息处理时的bug,这个出现的概率要高的多。 
• 
Caoyuan 
July 13th, 2009 at 20:20 | #5 
Reply | Quote 
dsun : 
》错,这种情况下Erlang会完全地垮掉,在OS中消失,留下一个大大的dump文件。 
我可没说你那个log问题,我说的是消息处理时的bug,这个出现的概率要高的多。 
哦。 
Scala的Actor彼此之间也可以(应该说推荐)传immutable的数据,一个actor垮掉,其它的actors照干嘛干嘛,为什么认为只有Erlang才能做得到? 
Scala现在缺的就差基于NIO的remote actor了。现有的remote actor还没有人在实际中真正应用。好在,这个已经提上议事日程。 
• 
dsun 
July 13th, 2009 at 20:29 | #6 
Reply | Quote 
》Scala的Actor彼此之间也可以(应该说推荐)传immutable的数据,一个actor垮 
》掉,其它的actors照干嘛干嘛,为什么认为只有Erlang才能做得到? 
Erlang在语言层面上可以保证,Scala则靠应用开发者去设计保证,容易出错,Scala的actor从本质上还是共享内存的。因此,我说在软件容错上,Erlang和其他语言的差别就是工程化和非工程化的差别 
• 
Caoyuan 
July 13th, 2009 at 20:40 | #7 
Reply | Quote 
dsun : 
》Scala的Actor彼此之间也可以(应该说推荐)传immutable的数据,一个actor垮 
》掉,其它的actors照干嘛干嘛,为什么认为只有Erlang才能做得到? 
Erlang在语言层面上可以保证,Scala则靠应用开发者去设计保证,容易出错,Scala的actor从本质上还是共享内存的。因此,我说在软件容错上,Erlang和其他语言的差别就是工程化和非工程化的差别 
Scala在语言层面保证immutable的例子: 
val a = List(1, 2, 3) 
a 是immutable的。 
特别地,在Actor消息传递中,最常用的case class: 
case class People(arg:Int, scope:Int, id:Int) 
actor1 ! People(40, 100, 00001) 
这个People(40, 100, 00001)一定就是immutable的。 
• 
Caoyuan 
July 13th, 2009 at 20:57 | #8 
Reply | Quote 
还有更多例子: 
传一个Map 
actor ! Map(1->”a”, 2-> “b”) 
传一个Tuple 
actor ! (”a”, “b”) 
传一个组合类型: 
case class People(age:Int, address:Address, name:String) 
case class Address(postcode:String, number:Int, street:String, city:String) 
actor ! People(40, Address(”10004″, 12, “长安街”, “北京”), “Caoyuan”) 
以上这些例子都是immutable的。 
与Erlang比,Scala的消息还是类型安全的。 
• 
dsun 
July 13th, 2009 at 21:02 | #9 
Reply | Quote 
@Caoyuan 
用immutable来避免一些微妙的并发问题确实是好的编程实践,但是immutable并不就等于软件容错,Erlang天生就是容错的,或者说Erlang就是为解决软件容错而生的,这方面其他语言确实没有可比性。 
为了容错,程序必须有独立执行绪的并发体组成(从某种意义上讲,并发的粒度就是容错的粒度),为了容错,并发体之间必须完全隔离,不共享任何东西 (也就是具有OS进程语义),为了容错,并发体间必须的能够通过某种方式link起来,以便一个并发体的死亡能够通知和其link的并发体,为了容错,并 发体之间只能通过纯消息的方式通信,为了容错,并发体应能够独立在线升级其所运行的代码。 
我说Erlang的工程化是指,在软件容错方面,Erlang对人的能力依靠相比其他语言要少的多,或者说即使在软件容错方面出了问题,也主要是理解问题而不是实现问题 
• 
Caoyuan 
July 13th, 2009 at 21:12 | #10 
Reply | Quote 
dsun : 
@Caoyuan 
用immutable来避免一些微妙的并发问题确实是好的编程实践,但是immutable并不就等于软件容错,Erlang天生就是容错的,或者说Erlang就是为解决软件容错而生的,这方面其他语言确实没有可比性。 
为了容错,程序必须有独立执行绪的并发体组成(从某种意义上讲,并发的粒度就是容错的粒度),为了容错,并发体之间必须完全隔离,不共享任何东西(也就是 具有OS进程语义),为了容错,并发体间必须的能够通过某种方式link起来,以便一个并发体的死亡能够通知和其link的并发体,为了容错,并发体之间 只能通过纯消息的方式通信,为了容错,并发体应能够独立在线升级其所运行的代码。 
我说Erlang的工程化是指,在软件容错方面,Erlang对人的能力依靠相比其他语言要少的多,或者说即使在软件容错方面出了问题,也主要是理解问题而不是实现问题 
首先,Erlang的process间share nothing,是靠immutable保证的,或者说,如果Scala的Actor之间传的是immutable的东西,那么它们就可以说是share nothing。这个跟Erlang是一样的。 
其次,Actor是由thread来实际执行的,因为Actor之间share nothing,所以thread之间也没有共享什么(虽然JVM允许thread之间共享数据,但是,如果你使用Scala的actor来实现并行,就能保证share nothing) 
在次,Scala的actor之间也可以link起来,与Erlang的每什么区别。 
我说过,Scala现在唯一缺的是一个**高效**的remote actor实现。 
我前面说的容错是建立在我说的Joe论文的两条精髓上面就是这个意思。 
• 
jackyz 
July 13th, 2009 at 21:15 | #11 
Reply | Quote 
dsun : 
fsong : 
jackyz : 
@fsong,建议你先去阅读大拿翻译的《Joe Thesis》 
确实不知道还有人翻译了,汗.学习. 
这个不是我翻译的。呵呵 
汗! ID 匹配失败。 
• 
dsun 
July 13th, 2009 at 21:23 | #12 
Reply | Quote 
@Caoyuan 
share nothing和immutable没啥关系。Erlang的Process和Scala的Actor根本区别在于Erlang的process之间是独 立的内存空间,Scala的Actor之间则是共享内存空间。JoeArmstrong说过,functional不functional不是 Erlang的核心,其核心就是为解决软件容错而生的具有独立内存空间的进程模型以及pure message passing style,至于进程这个黑盒子内运行什么语言,不重要。 
》我前面说的容错是建立在我说的Joe论文的两条精髓上面就是这个意思。 
容错是系统需求,你说的两条精髓是手段,这个要搞清楚。 
• 
Caoyuan 
July 13th, 2009 at 21:26 | #13 
Reply | Quote 
再说一个机制: 
Erlang的processes之间传消息除了比较大的binary,其它的消息都是拷贝一份给接受的process,这种实现是再垃圾回收和性 能之间的一个权衡决定:如果传的是一份新的拷贝,那当接受的process运行完毕,直接再内存里清掉就行了,GC的工作简单。如果传的一个 reference(比如大的binary块),那么就还要去数这个reference的引用计数,情况复杂些。
Scala通常传reference,与Erlang的binary传递类似,垃圾回收用JVM的行了。 
• 
dsun 
July 13th, 2009 at 21:31 | #14 
Reply | Quote 
@Caoyuan 
说到GC,就不得不提一下,Erlang的GC是per process的,JAVA的是per JVM的,这个对性能的影响也是蛮大的。 
• 
Caoyuan 
July 13th, 2009 at 21:36 | #15 
Reply | Quote 
@dsun 
》Scala的Actor之间则是共享内存空间 
你指的是物理实现吗?Erlang的VM里也有堆,Erlang的process之间也可以共享ets的。Scala的Actor之间是不是共享数 据(这里我特意不提“共享内存”)也是由编程人员决定的。你可以在Erlang里选择使用ets和传binary,也可以在Scala中选择传 immutable的数据,既然是imuutable的,actor之间就是状态独立的,我们通常说的share nothing,更准确地说应该是不共享可变的状态。绝对的share nothing是不存在的,至少,它们共享一个物理内存,硬盘空间、屏幕和键盘以及网卡吧。 
• 
Caoyuan 
July 13th, 2009 at 21:40 | #16 
Reply | Quote 
dsun : 
@Caoyuan 
说到GC,就不得不提一下,Erlang的GC是per process的,JAVA的是per JVM的,这个对性能的影响也是蛮大的。 
最终,综合的性能,JVM比Erlang强大,而同时,Scala是目前所有借鉴Erlang消息和进程机制的语言里最好的,因为它在语法、Actor库等方面提供了实现Erlang机制的几乎所有要件。 
• 
dsun 
July 13th, 2009 at 21:52 | #17 
Reply | Quote 
@Caoyuan 
》你指的是物理实现吗? 
我指的是一个并发体是否能够直接访问另外一个并发的私有数据,在Scala中你还是得考虑thread-safe的问题。 
摘自《programming in scala》: 
… 
The key way the actors model addresses the difficulties of the shared data and 
locks model is by providing a safe space—the actor’s act method—where 
you can think sequentially. Put another way, actors allow you to write a 
multi-threaded program as a bunch of independent single-threaded programs 
that communicate with each other via asynchronous messaging. This simplification 
works, however, only so long as messages are the only way you 
let your actors communicate.3 
For example, a GoodActor could include a reference to itself in a message 
to a BadActor, to identify itself as the source of that message. If 
BadActor invokes some arbitrary method on GoodActor instead of sending 
it a message with ‘!’, however, problems may ensue. The invoked method 
might read private instance data in GoodActor, which may have been written 
by a different thread. As a result, you would need to ensure that both the 
BadActor thread’s reading of the instance data and the GoodActor thread’s 
writing of it are synchronized on the same lock. The GoodActor’s private instance 
data has become shared data that must be guarded by a lock. As soon 
as you go around the message passing scheme between actors, therefore, you 
drop back down into the shared data and locks model, with all the difficulties 
you were trying to avoid in the first place by using the actors model. 
… 
• 
dsun 
July 13th, 2009 at 21:54 | #18 
Reply | Quote 
》最终,综合的性能,JVM比Erlang强大, 
什么样的综合性能? 
》而同时,Scala是目前所有借鉴Erlang消息 
》和进程机制的语言里最好的,因为它在语法、Actor库等方面提供了实现Erlang机制 
》的几乎所有要件。 
只能说形式模仿的最像而已。 
• 
Caoyuan 
July 13th, 2009 at 22:05 | #19 
Reply | Quote 
dsun : 
》最终,综合的性能,JVM比Erlang强大, 
什么样的综合性能? 
》而同时,Scala是目前所有借鉴Erlang消息 
》和进程机制的语言里最好的,因为它在语法、Actor库等方面提供了实现Erlang机制 
》的几乎所有要件。 
只能说形式模仿的最像而已。 
综合性能可以是:调度+计算+GC 
只是形式模仿的像吗? 
要实现Erlang的机制,要素由: 
语法层次: 
immutable变量语法 
immutable的集合类型,如List, Map, Tuple 
对消息的pattern match 
DSL能力 
以上要素是Scala弄Actor库之间就有的。等到想做的时候,在语言本身以外实现了一个Actor库就成了。 
类似的机制用Java、Ruby、C#都只能做到模仿,只有Scala做到了“拿来”。 
最后,我得忙其它的事了,我的砌楼任务就此结束。 
• 
dsun 
July 13th, 2009 at 22:20 | #20 
Reply | Quote 
@Caoyuan 
等Scala做到了hot-code swapping和selective receive后,再说实现了Erlang的几乎所有要件吧。你列举的不就是一些形式上的东西吗。 
呵呵,闲来无事一折腾,没想到折腾了这么一幢高楼。娱乐时间结束。 
• 
Caoyuan 
July 13th, 2009 at 22:26 | #21 
Reply | Quote 
dsun : 
@Caoyuan 
等Scala做到了hot-code swapping和selective receive后,再说实现了Erlang的几乎所有要件吧。你列举的不就是一些形式上的东西吗。 
@dsun 
啊哈:“不就是”,“太简单“… 老兄看来深刻呀。 
• 
Caoyuan 
July 13th, 2009 at 23:21 | #22 
Reply | Quote 
刚看到的一条消息,觉得有必要提一下: 
http://www.nabble.com/ANN%3A-Akka-Actor-Kernel%3A-RESTful-Distributed-Persistent-Transactional–Actors-td24453255.html 
关于Scala的Actor库,大意是: 
Erlang-style的Actor superviser体系和语法、容错。 
Software Transactional Memory (STM) ; 
Actor可以在remote node上定义和启动等等 
这个作者打算在Scala上实现Erlang-OTP 
• 
dsun 
July 14th, 2009 at 08:36 | #23 
Reply | Quote 
》Erlang-style的Actor superviser体系和语法、容错。 
没有并发体之间真正的隔离作为基础,这些也只能是形式。我相信在不远的将来,肯定会出现一堆一堆的所谓最佳实践和模式出来。 
》Software Transactional Memory (STM) ; 
没有像Haskell一样超强的类型系统,不知道这个STM会做成什么样子 
》这个作者打算在Scala上实现Erlang-OTP 
哈哈,卫星上天了。 
• 
dsun 
July 14th, 2009 at 08:47 | #24 
Reply | Quote 
@啊哈:“不就是”,“太简单“… 老兄看来深刻呀。 
哈哈,我可没说太简单,我觉得形式上很复杂。先天的不足,后天是补不来的。 
• 
Jeffrey Zhao 
July 14th, 2009 at 09:03 | #25 
Reply | Quote 
以上要素是Scala弄Actor库之间就有的。等到想做的时候,在语言本身以外实现了一个Actor库就成了。 
类似的机制用Java、Ruby、C#都只能做到模仿,只有Scala做到了“拿来”。 
最后,我得忙其它的事了,我的砌楼任务就此结束。 
其实对于Scala Actor的关键,例如调度,分配,容错,STM等功能,Java、C#也可以完全实现和使用。 
Scala的优势在于它的语法,例如pattern match,不过这只是涉及到消息的执行。 
我目前在为C#实现一个Actor模型,其中消息执行方式会有些特殊,但是效果不错,很适合C#。 
之于F#,我还在考虑使用什么样的API形式。 
• 
Jeffrey Zhao 
July 14th, 2009 at 09:05 | #26 
Reply | Quote 
dsun : 
@Caoyuan 
等Scala做到了hot-code swapping和selective receive后,再说实现了Erlang的几乎所有要件吧。你列举的不就是一些形式上的东西吗。 
呵呵,闲来无事一折腾,没想到折腾了这么一幢高楼。娱乐时间结束。 
selective receive在C#和F#里都不是问题,因为有比较特别的语言特性可以保证,而hot-code swapping可能比较困难,我也正在尝试着使用一些动态语言的执行方式,看看是否有所帮助。 
• 
litaocheng 
July 14th, 2009 at 09:27 | #27 
Reply | Quote 
@dsun 
@Caoyuan 
你们都先休息一下,快自报家门吧。让大家好好的认识一下。 
咱们好多不明真相的群众都迫切想知道“内幕”哦,呵呵。 
请二位介绍一下哦,作为本帖的预计结案陈词哈哈。 
希望这样的讨论更多,更激烈! 
俺代表三五个群众谢谢你们! 
• 
jackyz 
July 14th, 2009 at 10:54 | #28 
Reply | Quote 
这是标志性的第 128 楼,前面貌似有几楼被 wordpress 给 anti spam 了,正在严重考虑开个 BBS 的必要性……。 
dsun 同学还是惊暴一下内幕,要不然,不明真相的群众越聚越多,搞出社区的群体性事件那就瞎菜了。哈哈。。。 
• 
kebo 
July 14th, 2009 at 10:58 | #29 
Reply | Quote 
建一个bbs 吧 
• 
xushiwei 
July 14th, 2009 at 12:48 | #30 
Reply | Quote 
我在这里已经转了这个讨论: 
http://groups.google.com/group/ecug/browse_thread/thread/59717a9a705f6ec6 
• 
dogstar 
July 14th, 2009 at 19:30 | #31 
Reply | Quote 
@dsun 
同意@dsun的说法.如果一次1000个重计算的任务,这样单台机器明显扛不住,然后先到先得,后到后得,要不就超时抛弃.这个是业务需求,完全可以做 一个生产消费者模型去做.后面超时了扔掉.这个业务需求,是设计,不能靠语言提供的机制进行hack的实现.如果真需要并发处理1000个重计算任务,只 能加机器.对scala,erlang是没区别的. 
• 
Caoyuan 
July 14th, 2009 at 21:35 | #32 
Reply | Quote 
对 于明确的需求,当然应该有相应的设计。需不需要设计,这不是我本来想在这讨论的问题,我的目的是介绍Erlang和Scala的actors调度机制,而 且我认为了解这些机制有助于在面对不同的需求时决定选择的平台和技术,也有助于在实际遇到问题时帮助寻找导致问题出现的原因。 
这个话题起自对Erlang类型的讨论,引出是否适合企业应用,引出Erlang和典型的静态类型语言Scala的比较,引出Erlang和 Scala的Actor调度机制的比较。其实我一直想找机会介绍这两种不同的调度机制,因为在很多将Erlang用于Web应用的例子中(可能是好的选择 也可能是糟糕的选择),有很多人认为Erlang是解决并发问题的银弹,特别地,认为Erlang的独特的VM是取得这种效果的关键,而JVM的 Thread机制是不能解决这个问题的。但我不这么认为,我的观点是: 
Actors之间通过异步消息传递来通讯+Actors之间不共享可变的状态=一种好的并发和容错机制 
因此,当一种语言(比如Scala)和运行环境(比如JVM)满足上述条件时,自然也就能获得相似的效果。 
前面说过,Scala在语法和支持库上对immutable的变量、实例(instance)的支持是Scala能保证传递的消息是 immutable的关键。这些设计是Scala的作者深思熟虑的结果,Scala中的pattern match也能大大简化消息派发的代码,而类型安全的消息则是额外的收获。 
比如immutable的消息类型中,Scala有scala.colletion.immutable包,其中的List集合类,有人不明白为什 么要设计成head:tail连接的,其实目的就是为了保证immutable的List在分解后的各部分的依旧保持immutable特性、并兼顾了实 现的效率。 
在Java中,尽管有final关键字: 
final List a = new ArrayList() 
但我们知道 
a.set(0, newValue) 
还是可以的,就是说,没办法保证List中的内容的immutable。 
但在Scala中: 
val a = List(1, 2, 3) 
定义了一个immutable变量a以及immutable的List(1, 2, 3),如果试图: 
a(0)=newValue 
编译器会告诉你这是不允许的,阻止程序编译通过。 
这些immutable的集合类在Java和C#等语言中也可以实现,但我们要理解为什么实现这些类对于Erlang style的Actor机制很重要。 
还有pattern match在消息派发中的好处,用过Erlang都印象深刻。程序中如果很大一部分在处理消息派发,这些语法上的好处就显得非常重要的。 
在JVM上实现hot-swap也有相应的解决办法,实际上在servlet实现中已经相当成熟了,但为什么我们不认为Java语言在这一点上可以 和Erlang相提并论呢?因为Java的程序通常都被写成很强烈的OO倾向,正在运行的内存中的状态和状态之间的关系可能已经相当地复杂,要实现平顺地 hot-swap自然相当地困难。但对于Scala,如果你想获得hot-swap的好处,你可以选择functional style,并且尽量应用immutable变量。当然,如果系统并不复杂,比如简单的Web应用,http请求之间通常只共享了一个session,那 么,只要处理好session,做hot-swap就是了。 
再些这么些,就算是对litaocheng的回应,权当一个结语吧。 
• 
Caoyuan 
July 14th, 2009 at 22:06 | #33 
Reply | Quote 
dsun : 
@Caoyuan 
》你指的是物理实现吗? 
我指的是一个并发体是否能够直接访问另外一个并发的私有数据,在Scala中你还是得考虑thread-safe的问题。 
摘自《programming in scala》: 
… 
For example, a GoodActor could include a reference to itself in a message 
to a BadActor, to identify itself as the source of that message. If 
BadActor invokes some arbitrary method on GoodActor instead of sending 
it a message with ‘!’, however, problems may ensue. The invoked method 
might read private instance data in GoodActor, which may have been written 
by a different thread. 
… 
我想我说得很清楚了, 
当: 
1、actor之间只通过异步消息传递通讯(“调用另一个actor的方法”也叫通讯,这里不鼓励); 
2、actor之间传递的是immutable消息。 
时,这些actors(或者叫并发体)之间就会互不干涉,就是线程安全的。 
反过来,如果Erlang的prcocess之间都去读写同一个ets,这时这些process之间就共享了可变的数据,就是不安全的,这时也需要使用lock,也会有JVM上的相同的缺点。 
• 
dsun 
July 14th, 2009 at 22:42 | #34 
Reply | Quote 
@Caoyuan 
如果没有理解错误的话,是您把本该属于设计的问题归结为Erlang的缺陷,比如您举得那个log的问题,您的结论是Erlang prcoess调度的缺陷 
。这类问题属于系统的性能极限问题,本该进行规划和测试验证,要有必要的流控机制,并要设计系统在达到性能极限时的优雅降级策略。我 
的建议是应用开发者不要去关注(也不应该关注)底层平台的调度机制,而要关注系统的范围(功能,性能范围),并作出明确的设计策略。 
当然兴趣大的可以去研究,但是小心,这很危险,因为一旦觉得自己搞清楚了,就会在设计时不由自主的偷懒。 
》》Actors之间通过异步消息传递来通讯+Actors之间不共享可变的状态=一种好的并发和容错机制 
这个等式不成立,应该是: 
pure message passing + process间完全隔离 + link = 容错。当然上面的那个等式对于Scala来说已经不容易了。 
》前面说过,Scala在语法和支持库上对immutable的变量。。。 
不要老是强调immutable,对于容错来说它根本不是关键,当然对于Scala来说可能是根救命套草。所以我说,这是为了容错,在先天不足的JVM 
上打的补丁。 
》在JVM上实现hot-swap也有相应的解决办法。。。 
动态代码加载在JVM的新的规范中是有更好的支持,不过我不知道如何做到多个Actor同时运行同一模块(类)的不同版本。一个玩具程序说明不了问题,在 分布式环境中,经过无数实践检验的复杂系统的安全(包括失败时版本回退)、平滑、不中断业务的升级才能说明问题。此外,老是让人去这样做,去那样做,就说 明语言在这方面的工程化缺失。 
》反过来,如果Erlang的prcocess之间都去读写同一个ets,这时这些process之间就共享了可变的数据,就是不安全的,这时也需要使用lock》,也会有JVM上的相同的缺点。 
这个咱不抬杠。 
• 
dsun 
July 14th, 2009 at 22:44 | #35 
Reply | Quote 
编辑的时候漏了一个,等式应该是: 
pure message passing + process间完全隔离 + link + hotcodeswapping = 容错 
• 
Caoyuan 
July 14th, 2009 at 23:19 | #36 
Reply | Quote 
》如果没有理解错误的话,是您把本该属于设计的问题归结为Erlang的缺陷, 
》比如您举得那个log的问题,您的结论是Erlang prcoess调度的缺陷 
应该说,我的结论是:那个log出问题的原因,是:Erlang process的调度和Erlang把它设计成了singleton process。 
这就好比我在说这个桃不够甜是因为它还没熟。而你告诉我说,如果想尝甜的东西不如直接吃糖。我想这是我们讨论同一个话题时各自脑海中的上下文不同引起的。 
我也同意在erlang中解决这个问题可以依靠设计,就象我前面提到的,我们的最终方案是:同步log进程。但是,这是在我真正弄清Erlang的 调度机制以及log实现成了一个singleton process之后才做的针对性设计。在先前的测试中,在2个CPU到4CPU恰好是没问题,而scale到8个CPU时才出的,而且,我们当初并没有想 到Erlang的log设计成了一个异步机制,这个问题Ulf也照样犯。我很高兴我找到了这个问题的原因,并写了一个Blog把它共享,我也很高兴受到了 一些email感谢我提醒了他们。 
》不要老是强调immutable,对于容错来说它根本不是关键。 
您说要看joe的论文n遍,我建议再想joe的论文n遍,除了知道Joe说了什么,也理解Joe为什么这么说。如果不是为了 immutable,Erlang为什么只允许单次赋值,为什么要到处用函数,为什么在guard函数中只允许若干仔细挑选和实现的函数。如果 processA传给processB一个可变的binary,然后processA和processB同时去改这个binary,那么Erlang还能 说process之间没有耦合、死掉一个process没关系吗?Erlang还有那么容错吗? 
》动态代码加载在JVM的新的规范中是有更好的支持, 
》不过我不知道如何做到多个Actor同时运行同一模块(类)的不同版本。 
用不同的类加载器。 
• 
dsun 
July 15th, 2009 at 08:47 | #37 
Reply | Quote 
》应该说,我的结论是:那个log出问题的原因,是:Erlang process的调度和 
》Erlang把它设计成了singleton process。 
我觉得您把原因归结于此对Erlang不公,而且对其他开发者会产生误导。并且您把log更改为同步的做法也是个补丁的做法。您发现了问题,并不代表您给 出的原因和建议都是合适的。我前面说过,任何在高强度下运行的系统必须有明确的性能范围限定和服务降级策略,不Crash只是个最低要求。 
》您说要看joe的论文n遍,我建议再想joe的论文n遍,除了知道Joe说了什么,也理 
》解Joe为什么这么说。如果不是为了 immutable,Erlang为什么只允许单次赋值, 
》为什么要到处用函数,为什么在guard函数中只允许若干仔细挑选和实现的函数。 
呵呵,请认真阅读论文地第2章,算了,我还是贴出来吧: 
2.6 Language requirements 
The programming language which we use to program the system must 
have: 
• Encapsulation primitives — there must be a number of mechanisms 
for limiting the consequences of an error. It should be possible to 
isolate processes so that they cannot damage each other. 
• Concurrency — the language must support a lightweight mechanism 
to create parallel process, and to send messages between the processes. 
Context switching between process, and message passing, 
should be eecient. Concurrent processes must also time-share the 
CPU in some reasonable manner, so that CPU bound processes do 
not monopolise the CPU, and prevent progress of other processes 
which are “ready to run.” 
• Fault detection primitives — which allow one process to observe another 
process, and to detect if the observed process has terminated 
for any reason. 
呵呵,immutable只是个在美味上增加点风味吧了。 
》如果 processA传给processB一个可变的binary,然后processA和processB同时 
》去改这个binary,那么Erlang还能说process之间没有耦合、死掉一个process没 
》关系吗?Erlang还有那么容错吗? 
没看到我公式中写的 pure message passing吗? 
》用不同的类加载器。 
这也是个补丁,而且是个没经过检验的补丁 
我说钢筋混凝土的楼房结实,您说还是土房子解释,看,上海的钢筋楼房不是也倒了吗,土房子加几根木桩固定还是很结实的吗。呵呵! 
• 
dsun 
July 15th, 2009 at 08:47 | #38 
Reply | Quote 
》应该说,我的结论是:那个log出问题的原因,是:Erlang process的调度和 
》Erlang把它设计成了singleton process。 
我觉得您把原因归结于此对Erlang不公,而且对其他开发者会产生误导。并且您把log更改为同步的做法也是个补丁的做法。您发现了问题,并不代表您给 出的原因和建议都是合适的。我前面说过,任何在高强度下运行的系统必须有明确的性能范围限定和服务降级策略,不Crash只是个最低要求。 
》您说要看joe的论文n遍,我建议再想joe的论文n遍,除了知道Joe说了什么,也理 
》解Joe为什么这么说。如果不是为了 immutable,Erlang为什么只允许单次赋值, 
》为什么要到处用函数,为什么在guard函数中只允许若干仔细挑选和实现的函数。 
呵呵,请认真阅读论文地第2章,算了,我还是贴出来吧: 
2.6 Language requirements 
The programming language which we use to program the system must 
have: 
• Encapsulation primitives — there must be a number of mechanisms 
for limiting the consequences of an error. It should be possible to 
isolate processes so that they cannot damage each other. 
• Concurrency — the language must support a lightweight mechanism 
to create parallel process, and to send messages between the processes. 
Context switching between process, and message passing, 
should be eecient. Concurrent processes must also time-share the 
CPU in some reasonable manner, so that CPU bound processes do 
not monopolise the CPU, and prevent progress of other processes 
which are “ready to run.” 
• Fault detection primitives — which allow one process to observe another 
process, and to detect if the observed process has terminated 
for any reason. 
呵呵,immutable只是个在美味上增加点风味吧了。 
》如果 processA传给processB一个可变的binary,然后processA和processB同时 
》去改这个binary,那么Erlang还能说process之间没有耦合、死掉一个process没 
》关系吗?Erlang还有那么容错吗? 
没看到我公式中写的 pure message passing吗? 
》用不同的类加载器。 
这也是个补丁,而且是个没经过检验的补丁 
我说钢筋混凝土的楼房结实,您说还是土房子结实,看,上海的钢筋楼房不是也倒了吗,土房子加几根木桩固定还是很结实的吗。呵呵! 
• 
dsun 
July 15th, 2009 at 08:59 | #39 
Reply | Quote 
再写得明确些吧: 
》如果 processA传给processB一个可变的binary,然后processA和processB同时 
》去改这个binary,那么Erlang还能说process之间没有耦合、死掉一个process没 
》关系吗?Erlang还有那么容错吗? 
pure message passing = message passing+ message copy 
• 
dsun 
July 15th, 2009 at 09:21 | #40 
Reply | Quote 
做个结语吧。 
我为何喜欢Erlang,除了Erlang的味道特别合我胃口外,还有更重要的一点,那就是我认为Erlang是真正软件工程的产物。软件工程核心不是一 些所谓的过程方法、条条框框,而是基于对要解决问题领域的深刻理解,得出领域需求,并转化给系统需求,然后设计出最适合实现这些需求的语言需求,并把语言 实现出来,这才是软件工程的核心。这样的结果就是一个新手在使用这样的语言在这个领域进行开发和不使用这样的语言进行开发相比,生产力可以达到N倍的提 升,完全是客观的。Erlang语言就是这样的一个产物。Erlang只是应对高可靠、高度容错问题领域的一种参考实现罢了。 
从这个意义上来说,要想成为优秀的程序员,必须要有识别问题领域并把其转化为合适的语言特性,然后把语言实现出来的能力,优秀的程序员都应该是语言的设计者。当然,那些盲目的、没有目标的到处借鉴语言特性的情况除外。 
• 
albertlee 
July 15th, 2009 at 10:33 | #41 
Reply | Quote 
关于弱类型~ 正好有个 Haskell 的强类型可以进行互补。 
由Haskell 开发作为 Erlang网络的计算节点, 可以充分发挥 Haskell 在计算方面的方便特性,它的强类型和类型推导等都对程序正 
确性有很大帮助。 
Erlang 负责网络分布设施。取长补短。 
因此不必纠缠于Erlang的弱类型是否是完美的,只要它在其领域位置上能发挥好就可以了,不能指望一种特性包治百病。 

原创粉丝点击