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相信自己的判斷,相信自己的邏輯。?
?
評論
查看更多