Erlang是什么?
Erlang编程语言最初目的是进行大型电信交换设备的软件开发,是一种适用于大规模并行处理环境的高可靠性编程语言。随着多核处理器技术的日渐普及,以及互联网、云计算等技术的发展,该语言的应用范围也有逐渐扩大之势。
每一门编程语言的推出都是有目标的。大多数的目标是为了:提升性能,提升开发者表现力,提升开发者生产力,如Haskell,Python,Go等都是以这三点为主要设计核心的。
Erlang的推出则是为了打造一款高并发高扩展性的实时系统。例如应用于电信,银行,商业,即时通讯等场合。Erlang的运行时系统内置了对并发,分布式,容错等的处理。可见与其它以速度,简洁,表达性见长的语言相比,Erlang无疑就是只默默无闻的丑小鸭。
Erlang的容错能力是它与生俱来的的能力,因为这是电信领域的重要需求。但是这并不代表Erlang是复杂的、不具表达力的。
语法
Erlang的语法是很简单的,总共只有550行代码。因此尽管erlang语法与主流语言比有所不同,但是掌握起来也是很轻松地。连贯性是它的亮点,请看如下代码:
我们在erlang shell中执行它
再来看一个复杂一点的代码:
这个例子里使用了模式匹配功能,稍后再详细讲。现在尝试调用fac函数:
怎么样?它的语法不复杂吧,我想只有Lisps可与之媲美简单性。
表达性
当说到Erlang表达性的时候,第一时间想到的可能是消息传递,过程创建和管理。然而这里的重点是模式匹配,特别是接收消息后,模式匹配可使其处理变得非常简便。请相当这个以下这个模式匹配的例子:
首先,我们想访问Body和Headers变量。Shell提示的非常清楚:变量是非约束的。接下来尝试做更有趣的。
复制之前的代码后,接下来我们将会使用到http库(ibrowse):
然后执行shell
回到之前的代码里,
这里从ibrowse里调用了send_req函数。第一个参数是URL。第二场数是headers列表,即我们想发送的请求。在这里我们没有发送任何的header。最后,第三个参数是我们指定的请求动作。
接下来看看其执行结果:
其输出是包含4个元素的数组,{Term1,…,TermN}。Erlang数组与Python数组是很相似的。第一个元素是“ok”信息,说明程序执行正常。第二个元素是302,HTTP状态码302表示用户被重定向。第三个元素是google返回给我们的headers结果。
接下来尝试存储该结果:
其运作过程是:我们发送了一个请求给google,然后google对此进行响应并返回结果。ok是一个原子,302是一个字符串。他们都没有被指派,因为他们不是变量。但为什么这里使用了“=”号?原因在于Erlang中“=”号是作用不是指派。由于Erlang支持模式匹配机制,“=”在这里会作为匹配符来使用。它会尝试寻找“=”号左右两部分的共同点,然后把值与非约束变量进行绑定,因此最后会把值指派到正确的变量。
所以从ibrowse得到的结果是:send_req,Erlang会认为我们得到了一个以元素ok和302为开头的一个四元数组。然后因为Headers和Body是非约束的,所以把Google返回的对应内容进行指派。
也就是说,模式匹配是与它语言的switch或if/else/elif有着异曲同工之妙。
过程和消息
在函数式编程里函数的地位是举足轻重的。他们可以被指派到变量,或是作为参数传给其它函数或从其它函数里作为返回值返回。不妨把函数看作是其它类型。
在Erlang创建一个过程是很简单的,这里使用了内置的spawn函数进行过程创建。
Spawn的作用是创建新过程并返回PID过程标识。
如果你想针对消息进行处理,而不仅仅局限于在shell中检视结果,可以进行如下处理:
这里创建了一个Echofun函数,作用是接收消息并显示。
Erlang 适合做什么?
问题应该是Erlang不适合做什么,而不是Erlang可以做什么。都是图灵完整的语言,理论上你什么都能做,但是大多数人不会去拿Erlang开发操作系统,因为它不适合做这个。
那么,Erlang不适合做什么?
首先,数值计算。
Erlang是解释型的,虽然现在的解释器能够编译代码,但是还是太慢。有些量化的软件用Fortran写的都需要算好久,换用Erlang会让人等得想死。
再一个,业务逻辑非常复杂的系统。
这个说实话,Erlang真的不适合。Erlang最大的优势在于并发,以及并发系统的稳定性。这就是为什么很多电信系统后端在使用Erlang。业务逻辑复杂之后,系统设计时分离并发模块的时候就很难了,甚至有时候是为了并发而并发,这就没有意义了。
Erlang特别适合做io bound的高并发服务器。Erlang最大的优点就是轻量的线程,有极小的上下文交换(context switch)开销。比如如下的一段代码秒开100000个线程
[spawn(fun() -> 0 end) || _ <- lists:seq(1, 100000)]。
鉴于这个特性Erlang很适合做服务器。有一些很好的框架比如Cowboy性能非常不错。
Eric Moritz - Websocket Demo Results V2
现在越来越多的公司后端都开始尝试使用Erlang,它还有其他一些优势。
对RPC良好的支持,提供了简单高效的OTP框架帮助开发者快速实现功能
无间断部署,运行时支持代码交换。在Shell里一句make:all([load])无需重启服务就更新代码
在EC2和其他集群里简单的部署
垃圾回收
酷炫的语法 - 一行怒解析tcp包
<<4:4, 5:4, ToS:8, ToL:16, Id:16, Flags:3, FlagOffset:13, TTL:8, 6:8, Checksum:16, SrcIP:32, DstIP:32, Payload/binary>> = X.
Erlang像任何其他一门语言一样,当然是什么都可以做。不过它更适合做逻辑比较简单的服务端啦。
Erlang与Golang比较
我们都知道,无论是Python还是Ruby,甚至Java, 都是在解决业务层的问题,属于应用型语言,以解决业务逻辑为主,但还有一个领域是系统领域,偏网络层和底层操作,在这一块我一直在寻找一种优雅的方案,C++被我首先给淘汰掉了,C的开发效率太低,Java倒是比较合适,就是太臃肿,而且缺少系统编程的基因,竟它是企业级开发出身的。
Erlang, 它在网络层方面表现优秀,同时容错性和健壮性都很不错,它的虚拟机是唯一可以跟JVM媲美的,而且还有OTP的超重量级武器,几乎可以是通杀网络层应用,但根据我的总结它有一个硬伤和一个软肋,这一点后面展开,可以说选择Erlang是我目前所知道的方案里面是最优的。
Golang其实也蛮早的,大概08年的时候就知道Google在搞一门奇怪的语言,之后的几年一直有不少以老莫为代表的人在嘀咕Golang,其实我一直没太关注,我从ROR中吸取的经验是,成熟度对于商用很重要,后来基于Golang开发的产品越来越多,让我不得不去研究一下, 这我才知道,这就是我梦想中的Python,效率和性能达到了最佳的平衡,对Go了解越多, 就越不愿意用Erlang写代码,主要原因:
1、Erlang的硬伤在于代码的可读性、表现力,让我来举个小例子,比如你为你的系统软件构建一个RESTFUL的接口,我们大致了解一下代码风格, 用Erlang: https://github.com/extend/cowboy/tree/master/examples 用Go:http://code.google.com/p/goweb/wiki/GetStarted,先不说Erlang, 无论是你c/c++/python/ruby/java 出身, 对Go是不是有种很久违的感觉, 为什么说是硬伤? 因为对一门语言来说,语法是不大可能会大幅度变更的, 而且不会出现大的变化, 我不知道有没有人读过《松本行弘的编程世界>,里面阐述的道理很明白, 真正好的编程方式是人去主宰计算机而不是计算机主宰人, 我感觉Erlang就有点主宰我的编程思维的感觉(我的视力本身就不好,它还在不断的扼杀我的眼睛!), 编程首先是门逻辑学,其次是工程学,最后才是数学, 又让我想起吴军的《数学之美>所说的,人工智能上个世纪一直在走弯路, 期望机器的高度图灵完备, 而忽视人类本身已有的文明,统计归纳的应用。
2、Erlang的软肋在于高质量的库少,尽管有不少杀手级应用, 同样Go在这方面也是软肋, 这一点对于一个不到五年的语言有情可原, 但对于一个20多年的语言是不是有点说不过去, 比如你用json解析库,很多人都是从mochiweb这个基本不更新的库中去抽取, 而我认为对于类似json这种东西可以考虑融入到语言标准库中, 因为未来的商业软件的api化趋势越来越明显,说的难听点 , 一个倚老卖老一个与时俱进,反正我对Golang的库一点也不担心, 目前的成绩易经非常棒了, 远远优于Ruby/Python的前五年。
3、Erlang不合群,这主要体现在跟其他语言的交互性上, 当然这也有深层次的原因,Erlang本身有自己的哲学 如出错恢复机制, 你融入一个其他语言的东西进去,这帐就不好算,就好比你硬要让一个喝咖啡的跟一个吃大蒜的坐在一起,总之你写一个Erlang的port远远比Go复杂, 甚至比Python/Java还要复杂,这就造成了Erlang在底层编程上效果不是很好, 没法利用linux已有的很多优秀成果,我一直认为Erlang的什么的mysql/pg/oracle驱动都没有必要存在,Erlang一定是一个self-container应用,你只要用到了其他东西, 根据木桶理论,你就不敢号称9个9,以系统的眼光看问题,我觉得一个系统的鲁棒性不能依赖于某一组件, 这也是为什么爱立信本身的Erlang应用并不广泛。
4、说说数据类型吧, 我不止听到1个人说Erlang对字符串的处理不有好, 它把string当做list来处理,其实本质上是该这么,但,还是那句话, 违背了面向人的哲学, 应该做一些DSL,比如Golang里面的 := 就是一个糖衣, 等价于 var xx yyy = zzzz, 大大方便的程序员少敲不少字符, Golang里面对字符转可以说基本和python差不多,slice map函数很强大, 支持lambda条件,虽然Erlang的基本类型很少,但有很多构造,所谓构造等价于Golang里面复杂的struct, 也奇怪了,我就是感觉Erlang构造伤眼睛好吗?可能是各种括号的比对的原因吧, 而且我认为这是不必要的, 显然Erlang缺少DSL的基因, 当然跟Erlang出身的年代有关, 我不夸张的说,自打用Erlang以后我的视力又下降了100度左右, 我不是很喜欢lisp所说的符号也是一种语法, 可能这又跟函数式编程有关吧:形式推导远大于逻辑演绎。
5、其实我最不关注的是性能问题, 因为随着摩尔定律,单位计算单元的性价比会无限高,但Golang既然提出它的性能逼近C,那我还是提一下吧, 当然,Erlang也还可以, 虽然比Java慢但跟Python一个档次。
6、再谈谈报错机制, 因为Erlang的的报错信息太让人纠结了, 起初以为我不会看出错信息, 后来也使用了Sasl, 还是不够直观,甚至有时要用工具分析crash文件来定位问题,还是跟Erlang的哲学有关, 在Erlang中一切都是并行的, 所以它根本不care是物理哪一行出错, 只跟Actor绑定, 然后告诉你Actor的ID和出错代号, 你自己凭经验去分析吧,这样做的好处是可以很方便定位出并行中出现的问题,但凡事都是相对的, 在这一点上有点纠枉过正,根据我的经验, 绝大部分时候我只希望先给我明确的指出哪一行出错了好吗? 甚至把顺序的backtrace用完整的英文句子打印出来好吗?至于并行中的错误及时在命令式多线程语言中是不常见的,虽然并不是没有, 但遇到错误我再费劲去调试好了, 但并不是所有的逻辑都用并行的思维去定位问题, 我甚至认为, 对于一个系统不完全是并行也不完全是串行,跟好比我们衡量世界不能单纯的唯物也不能完全的唯心一样, 这一点Golang就做了很好的折中, 不需要并行的时候你老老实实的写串行代码, 需要并行的时候也有较复杂的机制来应对, 合乎情理。
7、再说说招人吧, 以前招过好几个C出来的人,说实话水平很好, 可以一周就完成一个小组件, libevent用的熟的很,后来我逼人家用Erlang,结果把人家逼走了,至今我还很后悔, 自己的一厢情愿强加在别人身上真是太不合适了,但我招纯Erlang出来的人,可以说比招objc的人还难, 没有人,空谈技术的优雅性首先就是不靠谱的,再看看邮件列表, Golang的活跃度明显比Erlang高很多,基本逼近Ruby,更重要的是,根本不担心Golang的人才,因为只要熟悉Python/C/Ruby/或者C++, 基本可以实现半天入门, 之后就可以噼里啪啦边搜资料边干活了,虽然有足够的深度,但门槛极其平缓,工程人员也可以复用很多已有的知识。 Erlang在这一点其实跟第一点硬伤有关,大部分人学一周都摸不着头脑,不是每个人的抽象思维和世界观都是一样的好吗, 所以函数式编程尽管不比命令式语言起步晚,但始终学的人很少,这就是历史, 对于大部分人, 更希望解决问题,创造价值,而不是数学来推导去。
8、最后我建议, 如果你是玩c/c++的, 现在开始学Golang,是最好的时机, 跟一门靠谱的语言一起成长, 这种感觉非常棒, 你用Erlang折腾1个应用, 用Go恐怕都完成了10个开源项目, 当然,也要结合自己的口味, Golang就是Sublime Text, Erlang就是Emacs相信自己的判断,相信自己的逻辑。
评论
查看更多