2014/1/27

erlang basics - data type

erlang 裡的 data 通常被稱為 term。以下內容涵蓋了 numbers and arithmetic, binaries and bitstring, atoms, tuples, lists, strings, pid, port, references, fun, comparing terms, record

numbers and arithmetic

  1. integer
    整數沒有大小限制,隨便怎麼用都可以。

     1> 100.
     100
     2> 1234567890*9876543210*99999999.
     1219326298933089578736473100

    可以用 2進位 到 36進位表示法,16# 就表示是 16進位。

     3> 16#FFff.
     65535
     4> 2#101010110.
     342
     5> 36#ZZ112ko.
     78305427528

    可以用 $ 得到任意字元的數值編碼,ASCII/Latin-1/Unicode 都可以。

     6> $9.
     57
     7> $\n.
     10
     8> $測試.
     * 1: illegal character
     8> $測.
     28204
  2. floating point
    採用64位 IEEE 754 double precision,規定一定要以數字開頭,不能以 . 開頭。erlang 沒有 sigle-precision floating point。

     9> 3.14.
     3.14
     10> -0.123.
     -0.123
     11> 5.312121e23.
     5.312121e23
     12> 19029.219021e-11.
     1.9029219021e-7
  3. arithmetic
    基本的算術為 + - *,如果其中有某一個數值為 floating point,則另一個也會轉換為 floating point 進行運算。除法有兩種 / 跟 div,/ 會回傳 floating point,而 div 會將小數部份截斷,rem 則是取得餘數。

     13> 2*3.14.
     6.28
     14> 2*4.0.
     8.0
     15> 2*4.
     8
     16> 4/2.
     2.0
     17> 4/3.
     1.3333333333333333
     18> 4 div 3.
     1
     19> 4 rem 3.
     1
     20> math:sqrt(2).
     1.4142135623730951
  4. bit operation
    bsl 是將位元資料往左移動,bsr 是往右,還有 band, bor, bxor, bnot。

     21> 1 bsl 4.
     16
     22> 1 bsr 4.
     0
     24> 1 bxor 4.
     5
     25> 1 bor 4.
     5
     26> 1 band(bnot 4).
     1
     27> bnot 4.
     -5

binaries and bitstring

binaries 就是 a sequence of bytes,也就是 8位元 byte 的序列,bitstring 是廣義的 binaries,長度不必是 8 的整數倍。binaries 是一個包含在 << ... >> 內,用逗號區隔的整數序列,可填入 0~255 的數值,超過時,例如 256 會自動轉換為 0。

28> <<0,1,2,3,255>>.
<<0,1,2,3,255>>
29> <<0,1,2,3,256>>.
<<0,1,2,3,0>>
30> <<0,1,2,3,255,256>>.
<<0,1,2,3,255,0>>
31> <<"hello", 32, "world">>.
<<"hello world">>
32> <<0,1,2,3,255,-256>>.
<<0,1,2,3,255,0>>
33> <<0,1,2,3,255,-2>>.
<<0,1,2,3,255,254>>

atoms

atom 類似 java 的 enum,只要兩個 atom 的字元全部一樣,就代表他們是完全相等的。不需要宣告就可以直接使用,他是由一連串的字元組成的,通常以小寫字母開頭,後面可以使用大小寫字母、數字、底線、@,如果還要用到其他字元,就要用單引號括起來,字元的長度上限為 255,單一系統的 atom 數量上限為 1048576 個。

atom 一旦被建立,除非系統 restart,否則就永遠不會被清除,長期運作的系統,要避免動態生成 atom。

35> ok.
ok
36> error.
error
37> trap_exit.
trap_exit
38> route77.
route77
39> test_atom.
test_atom
40> test@atom.
test@atom
41> '@!#!$@%'.
'@!#!$@%'
42> true.
true
43> false.
false
44> undefined.
undefined

tuples

以 { } 表示,是固定長度,依照順序排列的元素列表,裡面可放0到多個元素,也可以再放入另一個 tuple。 tuple 的第一個元素,通常是以 atom 來標記這個 tuple 存放的資料標記,也稱為 tagged tuple。

45> {1,2,3}.
{1,2,3}
46> {one,two,three}.
{one,two,three}
47> {one,{nested,"two", {string}}}.
{one,{nested,"two",{string}}}

48> {position, 5, 2}.
{position,5,2}
49> {size, 25}.
{size,25}

lists

以 [ ] 表示,可存放任意數量的 terms。

50> [].
[]
51> [1,2,3].
[1,2,3]
52> [[1,2,3], ["test", 4]].
[[1,2,3],["test",4]]

referential transparency

erlang 的 list 必須要遵循引用透明性 referential
transparency,因為 erlang 的變數的值不能修改,X 的 reference 可以傳遞到程式的任何一處,但 X 在每一個地方所參考到的值都是一樣,不會改變。此一特性的優點是:

  1. 大幅減少程式的錯誤,因為變數不會改變的特性
  2. 將單一 process 程式重構成多個 processes 時,不需要重寫 code
  3. 不存在對現有資料結構寫入的 operation,因此可以對內部記憶體與multi-processes處理進行更好的優化

新資料要加在 list 的左邊

為了達到 referential transparency 的緣故,新元素要加在 list 的左邊,元素的加法是用 | 。

兩個 list 的加法是用 ++ ,++ 右邊的 list 並不會被破壞,而是藉由一個 pointer,成為新 list 的一部分。

53> [1|[]].
[1]
54> [2|[1]].
[2,1]
55> [ 4,3,2 | [1] ].
[4,3,2,1]
56> [4,3,2] ++ [1,0].
[4,3,2,1,0]
57> [4] ++ [3,2,1,0].
[4,3,2,1,0]

[4,3,2] ++ [1,0] 依序 會處理 [ 2 | [1,0] ] -> [ 3 | [2,1,0]] -> [ 4 | [3,2,1,0]],因此左邊 list 的長度會決定 ++ 的耗時長短,實務上使用時,左邊的 list 長度要比較短,且要盡量從左邊加入新元素到列表中。

list 的結構

頭元素:head,tail會指向 list 其他的部份,nil 代表一個空 list。

list 通常用來存放臨時資料、中間結果、字串的cache。對於需要長期保存的資料,要改用 binary 來儲存。

(im)proper list

proper list 都是以 nil 作為 list 的結尾,而 [ 1 | ok ] 這種以 atom 作為結尾的 list 就是 improper list。

很多 list 處理的函數通常會以 nil 來作為結束的判斷,而這些函數如果傳入 improper list 就會造成系統 crash,因此要避免使用 improper list,如果在程式中看到了improper list,就代表可能程式寫錯了。

strings

erlang 的雙引號字串實際上就等同於 list,元素內容就是個字元的數值編碼。

erlang shell為了區分 string 與 list 會檢查 list 裡面所有的元素是不是能列印到 console 上,否則就列印為整數list,要強迫讓 shell 列印出實際的內容,可在前面加上 0 ,例如 [0|v(1)]。

1> "and".
"and"
2> "\t\r\n".
"\t\r\n"
3> [97,98,99,100].
"abcd"
4> [32,9,13,10].
" \t\r\n"
5> [$a,$b,$c,$d].
"abcd"
6> [0|v(1)].
[0,97,110,100]

pid, port, references

  1. Pid
    所有程式都需要一個 erlang process 才能運作,每一個process都有一個 Pid,shell 會以 <0.32.0> 的格式列印 Pid,在 shell 中可使用 self() 取得目前 process 的 Pid。

     8> self().
     <0.32.0>
  2. Port
    Port 跟 Pid 差不多,可跟 Erlang 外界通訊,但不具有程式碼的執行能力,格式為 #Port<0.472> 。

  3. Ref
    可用 make_ref() 產生一個 Ref,格式為 ,通常備用做各種保證資料唯一性的標籤。

     9> make_ref().
     #Ref<0.0.0.60>

fun

erlang 為 functional programming language,可將函數視為參數傳入另一個函數中,其資料型別為 fun,通常也可稱為 lambda or closure。

comparing terms

erlang 的 terms 可以透過內建的 <, >, == 比較與排序,不同資料型別的排序規則為

numbers < atom < ref < fun < port < pid < tuple < list < strings < binary

可使用 lists:sort 進行排序測試。

12> lists:sort([a,d,3,"test", [2,3],1, c, 3.2, "yy"]).
[1,3,3.2,a,c,d,[2,3],"test","yy"]

小於等於要寫成 =<
大於等於要寫成 >=
完全相等(同一個)要寫成 =:=
完全相等的否定(不是同一個) 要寫成 =/=
相等 ==
不相等 /=

不使用 30 =:= 30.0 ,改採用 30 == 30.0 算術相等來判斷。

13> 30 =:= 30.0.
false
14> 30 =:= 30.
true
15> 30 =/= 30.0.
true
16> 30 == 30.0.
true

99%的情況,要使用 =:=,只有在比較 floating point 與 integer 時,== 才有用。

在很多程式或函式庫中,會看到很多地方應該使用 =:= 但是卻是使用 ==,這不會造成程式的錯誤,因為 == 的引數,不是 floating point 而是 integer,兩者的行為是一樣的。

record

當 tuple 裡的元素變多時,我們就不容易記得,那個元素代表什麼意義,record 可以讓我們把名字跟特定的元素關聯起來,record 的本質就是有標記的tuple。

record 的定義宣告只能出現在 module 中,無法直接在 shell 裡面定義,在 shell 中,我們可以用 rr 的指令將定義讀進 shell 中。

rd(R,D) -- define a record
rf() -- remove all record information
rf(R) -- remove record information about R
rl() -- display all record information
rl(R) -- display record information about R
rr(File) -- read record information from File (wildcards allowed)
rr(F,R) -- read selected record information from file(s)
rr(F,R,O) -- read selected record information with options

record 的定義可以放在 .hrl 的檔案中,或是直接放在 .erl module 裡面,以下為定義的語法,可以直接在定義中,給予某些 key 預設值。

-record (Name, {
    key1 = DefaultValue1,
    key2 = DefaultValue2,
    key3,
    ...
})

範例

-record(customer, {name="<anonymous>", address, phone}).
-record(todos, {status=reminder, who=joe, text}).

我們可以用 rr 讀取 record 定義,然後再以 # 產生 record,因為單一賦值的限制,我們不能修改 record,但可以根據前一個 record,修改某個欄位後,產生一個新的 record。

(erlangotp@yaoclNB)12> rr("D:/projectcase/erlang/erlangotp/src/records.hrl").
[customer,todo]
(erlangotp@yaoclNB)13> X=#todo{}.
#todo{status = reminder,who = joe,text = undefined}
(erlangotp@yaoclNB)14> X1=#todo{status=urgent, text="fix errata in book"}.
#todo{status = urgent,who = joe,text = "fix errata in book"}
(erlangotp@yaoclNB)15> X2=X1#todo{status=done}.
#todo{status = done,who = joe,text = "fix errata in book"}
(erlangotp@yaoclNB)16> Y1=#customer{}.
#customer{name = "<anonymous>",address = undefined,
          phone = undefined}
(erlangotp@yaoclNB)17> Y2=#customer{name="test user", address="HomeTown", phone="987654321"}.
#customer{name = "test user",address = "HomeTown",
          phone = "987654321"}

一樣可以用 pattern matching 取出某個欄位的值

(erlangotp@yaoclNB)18> #todo{who=W, text=Text}=X2.
#todo{status = done,who = joe,text = "fix errata in book"}
(erlangotp@yaoclNB)19> W.
joe
(erlangotp@yaoclNB)20> Text.
"fix errata in book"

也可以直接用 . 的語法,取出某個欄位的值

(erlangotp@yaoclNB)21> X2#todo.text.
"fix errata in book"

當我們用 rf 將 record 定義忘記,原本的 record X2,就會變成 tuple,如果再次把 todo 的定義讀進來,X2 又會變回 record。

(erlangotp@yaoclNB)22> rf(todo).
ok
(erlangotp@yaoclNB)23> X2.
{todo,done,joe,"fix errata in book"}
(erlangotp@yaoclNB)24> rr("D:/projectcase/erlang/erlangotp/src/records.hrl").
[customer,todo]
(erlangotp@yaoclNB)25> X2.
#todo{status = done,who = joe,text = "fix errata in book"}

參考

Erlang and OTP in Action
Programming Erlang: Software for a Concurrent World

2014/1/23

Jerry's Journey to Scratch Part1

Jerry 從小一直說想要使用電腦,我們也不曉得是不是被我打電動的習慣影響,因此就被我們用各種理由阻止,但是今年 Jerry 在學校上了英文打字的課,每週一次,既然英文打字已經有些基礎了,我想也該是要讓他嘗試寫寫程式的時候了,屆時也會知道他是不是真的有興趣。

我是 Java Programmer,教他物件導向似乎是太誇張了,教他 Linux 變成一個 Hacker 距離好像又有點遙遠,因此就找了Scratch,Scratch是一套圖形化程式設計軟體,適合8歲以上的中小學生作為學習程式設計的入門軟體,它是由美國麻省理工學院媒體實驗室(MIT Media Lab)的終身幼稚園團隊(Lifelong Kindergarten Group)所開發的一種新的程式語言,可以透過Scratch官方網站 http://scratch.mit.edu/ 與人分享每個人的專案。

另外 scratch 有個 library S4A,可以讓scratch 跟 arduino uno開發板互動,將來應該可以透過這個方式,直接使用搖桿操控 scratch 的角色,或是其他跟外部元件互動的 prototype。

Scratch 目前的 2.0 版為 Beta 狀態,可以在 http://scratch.mit.edu/scratch2download/ 下載,她是用 Adobe AIR 實作的,標準的 1.4 版可在 http://scratch.mit.edu/scratch_1.4/ 下載,目前大部分書本的範例都是用這個版本,1.4 跟 2.0的介面看起來沒有什麼改變,2.0 版改用AIR,應該是為了要簡化跨平台開發的問題。

2013/12/17 Day 1: 40 mins

Jerry 第一次接觸Scratch,我讓他試著用 google 搜尋一些資料,先了解 scratch 的介紹,然後就到 Scratch官方網站 申請了他自己的帳號,接下來,就在網站上瀏覽其他人的專案。

不習慣切換視窗學習的Jerry,應該還是適合用書本學習,選了兩本書:程式設計邏輯訓練:使用Scratch <隨書附410分鐘影音教學檔> 、玩轉Scratch動畫遊戲製作,第一本最後一張有講到S4A。

2013/12/25 Day 2: 30 mins

訂的書第二本缺貨,一直沒能拿到書本,今天就先讓Jerry在網路上搜尋了 scratch 教學,自己安裝軟體,並按照教學內容,一步一步實作,讓小貓移動,發出聲音,更換背景,並用置換角色圖片的方式,做了一個簡單的三張圖片的動畫,今天用了半個小時學習。


2013/12/26 Day 3: 30 mins

觀看 Scratch遊戲動畫一把抓 第一課的教學影片,並下載執行第一課的範例檔。


2013/12/29 Day4: 70 mins

跟著 程式設計邏輯訓練:使用Scratch <隨書附410分鐘影音教學檔> 第一章的內容演練習題,需要另外解釋「訊息」的用途,書本是填為中文的訊息,但其實可以改成英文,也不需要跟聲音檔案一樣,因為打中文字比較慢,所以花的時間比較久。

2014/01/01 Day5: 60 mins

閱讀書本第二章,了解循序結構、重複結構。重複結構的練習中,有個畫出正方形圖形的練習,第一次他用面向右、往前100步、向上、往前100步...的方式,重複一次畫出正方形,第二次,我要求他改使用旋轉的方式,他用旋轉90度、往前100步、旋轉90度、往前100步...的方式,重複一次畫出正方形,第三次,我要求他要重複四次,他可以自己改成,旋轉90度、往前100步,重複四次,畫出正方形,另外做了賽跑的範例。

2014/01/04 Day6: 60 mins

做無窮迴圈魚缸的範例,這邊他沒注意到,移動只有往左或往右,而魚的游動是斜的,所以角色必須要加上旋轉 15 度。今天還把選擇結構做完,兩個遊戲範例,電流急急棒以及簡單的打地鼠。

2014/01/07 Day7: 30 mins

做了全域變數第一個範例,猴子吃香蕉。全域變數這個名詞是程式設計的專有名詞,我認為他只是看過而已,並沒辦法真正地了解「全域」的意義,短時間內也沒辦法講清楚,只能後續有範例時,再提醒他全域的概念。

2014/01/12 Day8: 30 mins

打老鼠,學習全域變數。

2014/01/18 Day9: 90 mins

學習角色變數的兩個範例:打蝙蝠跟魚缸。在學習魚缸程式時,他直接把「吃飽幾隻」跟「有飼料嗎」這兩個變數複製到三隻魚的角色中,很明顯把全域變數跟角色變數的觀念搞錯了。也因此造成餵魚之後,計算吃飽幾隻的結果一直都是錯誤的。

全域跟區域變數對一開始寫程式的人來說,的確是個很難理解的概念,尤其當變數名稱都一樣時,需要一些經驗才能判斷要不要把資料放在全域變數。



2014/01/20 Day10: 90 mins

練習 BMI 計算,完成第二章的練習,進入第三章。今天還用了一些時間,在官方網站看到一個 3D 迷宮的demo。


erlang basics - installation

安裝 erlang 開發環境並不難,以下我們分 Windows 與 CentOS 兩種作業系統說明。

Windows

在 erlang otp downloads 頁面下載安裝檔,32 bits: esl-erlang_16.b.3-1~windows_i386.exe 或是 64 bits: esl-erlang_16.b.3-1~windows_amd64.exe,然後直接執行就可以了,erlang會安裝到 c:\Program Files\erl5.10.4。

記得要設定環境變數,可在 電腦->內容->進階系統設定->環境變數 這裡設定:

ERLANG_HOME=c:\Program Files\erl5.10.4
PATH=%ERLANG_HOME%\bin;%PATH%

CentOS 6

CentOS 安裝有三種方式,第一種是 rpm 安裝,第二種是直接由 source code 編譯,第三種是從 CEAN 下載安裝檔。

rpm 安裝檔

在 erlang otp downloads 頁面下載安裝檔,首先取得 rpm 安裝檔,32 bits: esl-erlang_16.b.3-1~centos~6_i386.rpm 或是 64 bits: esl-erlang_16.b.3-1~centos~6_amd64.rpm,接下來依照下面的 sript 進行安裝。

# 因為 erlang 有些 gui 工具介面
yum install wxGTK
# 因為 rpm 相關的套件,openssl 安裝到最新版 1.0.1e-16.el6_5.1 即可
yum install openssl
# install erlang
rpm -Uvh esl-erlang_16.b.3-1~centos~6_i386.rpm
# 安裝的目錄為 /usr/lib/erlang

由 source 編譯

如果要從 source code 編譯,先到 erlang otp downloads 頁面下載原始檔 otp_src_R16B03.tar.gz。

tar zxvf otp_src_R16B03.tar.gz
cd otp_src_R16B03
./configure

執行後出現以下的警告,所以我們要先安裝一些套件
odbc : ODBC library - link check failed
wx : wxWidgets not found, wx will NOT be usable
documentation :
fop is missing.
Using fakefop to generate placeholder PDF files.

yum install fop unixODBC* openssl-devel
yum install wxGTK
./configure

執行後還是一樣,出現警告,這表示 wxGTK 套件要自己編譯
wx : wxWidgets not found, wx will NOT be usable

cd ..
wget http://sourceforge.net/projects/wxwindows/files/3.0.0/wxWidgets-3.0.0.tar.bz2/download
tar jxvf wxWidgets-3.0.0.tar.bz2
# 把剛剛裝的 wxGTK 移除
yum remove wxGTK
cd wxWidgets-3.0.0
# 安裝 opengl 相關套件
yum install mesa*
./configure --with-opengl --enable-debug --enable-unicode --enable-graphics_ctx 
make
make install
export LD_LIBRARY_PATH=/usr/local/lib
ldconfig

接下來就可以正常地把 erlang otp 裝好。

cd otp_src_R16B03
./configure
make
make install

CEAN

CEAN: Comprehensive Erlang Archive Network 將主要的 Erlang Application 都集中在一起,並提供各種 OS, CPU 編譯好的 binary files,應該是依照網頁的說明去安裝就好了,這種安裝方式,我沒有測試。

Erlang shell: erl

在 Windows 可以直接從 Menu 選單中執行 werl,這會出現 erlang 的 GUI console,也可以自己在命令提示字元中,執行 erl。

查閱 erlang otp 版本資訊

> erl -version
Erlang (SMP,ASYNC_THREADS,HIPE) (BEAM) emulator version 5.10.4

啟動 erlang shell,直接執行 erl 即可,畫面上的 1> 是 erlang shell 的系統提示符號,每一次輸入一個 expression 就會累加變成 2>,可以用上、下方向鍵或是Ctrl+P/Ctrl+N,直接上下切換剛剛輸入的每一行資料。erlang expression 每一句都是以 . 句號結束,不一定要在同一行裡面就把整個 expression 寫完,erlang shell 會馬上把計算的結果列印在畫面上。

> erl
Erlang R16B03 (erts-5.10.4) [source] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V5.10.4  (abort with ^G)
1> 40.
40
2> 12 +
2> 5.
17
3> h().
1: 40
-> 40
2: 12 + 5
-> 17
ok
4> v(1)+v(2).
57
5> "a test.".
"a test."

shell functions

在 shell 直接打 help(). 可以列印出 shell 可使用的函數,以下列出常用的函數 。

6> help().
** shell internal commands **
b()        -- display all variable bindings 列印所有變數連結
f()        -- forget all variable bindings  移除所有變數連結
f(X)       -- forget the binding of variable X 移除某個變數連結
h()        -- history
v(N)       -- use the value of query <N>
** commands in module c **
bt(Pid)    -- stack backtrace for a process
c(File)    -- compile and load code in <File>
cd(Dir)    -- change working directory

help()     -- help info

i()        -- information about the system
ni()       -- information about the networked system
i(X,Y,Z)   -- information about pid <X,Y,Z>

l(Module)  -- load or reload module
lc([File]) -- compile a list of Erlang modules
ls()       -- list files in the current directory
ls(Dir)    -- list files in directory <Dir>

m()        -- which modules are loaded
memory()   -- memory allocation information

nc(File)   -- compile and load code in <File> on all nodes
nl(Module) -- load module on all nodes

pid(X,Y,Z) -- convert X,Y,Z to a Pid
pwd()      -- print working directory
q()        -- quit - shorthand for init:stop()

quitting shell

要離開 shell 有幾種方式:

  1. 使用 q() 或 init:stop()
    q() 是 init:stop() 的簡寫,這是最安全的跳出方式。

  2. Ctrl-G
    Ctrl-G 可打開使用者命令選單,鍵入 Ctrl-G 的時候,會出現 User switch command --> 的提示字元,再鍵入 h 或 ? 可以列印所有的指令。

    這個功能在 linux 裡面的 erl 有作用,在 windows 的 werl 也有作用,但 windows 的 erl 卻是直接產生一個新的 shel。我還沒找到要怎麼在 windows erl 打開這個功能的熱鍵。

     6>
     User switch command
      --> h
       c [nn]            - connect to job
       i [nn]            - interrupt job
       k [nn]            - kill job
       j                 - list all jobs
       s [shell]         - start local shell
       r [node [shell]]  - start remote shell
       q                 - quit erlang
       ? | h             - this message
  3. BREAK
    在 linux 可以按 Ctrl-C,Windows 的 werl 可以按 Ctrl-Break,這會出現 BREAK 選單,進入選單後,再以 (a) 退出系統 (c) 返回 shell (v) 列印目前 erlang 的版本 (k) 可瀏覽所有 erlang 內部活動,並強制關閉某個故障的 process。BREAK 選單位於底層,可以在 Ctrl-G 之後,再呼叫出 BREAK 選單。

     BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
            (v)ersion (k)ill (D)b-tables (d)istribution

process處理

如果有某個 process crash,或不慎進入了無窮迴圈,我們可以在 Ctrl-G User switch command 裡面,進行處理。

以 timer:sleep(infinity) 模擬無窮迴圈的狀況。

Eshell V5.10.4  (abort with ^G)
1> timer:sleep(infinity).

這時候,shell 會被鎖死,不管打什麼指令都沒有反應,按 Ctrl-G 進入 user switch command。

j 列出目前的 process
s 啟動一個新的 process
c 2 連接到新的、正常的 process

User switch command
 --> j
   1* {shell,start,[init]}
 --> s
 --> j
   1  {shell,start,[init]}
   2* {shell,start,[]}
 --> c 2

進入第二個 process 後

k 1 把第一個掛掉的 process 砍掉
c 連接到目前預設的 process(有 * 記號)

Eshell V5.10.4  (abort with ^G)
1>
User switch command
 --> j
   1  {shell,start,[init]}
   2* {shell,start,[]}
 --> k 1
 --> j
   2* {shell,start,[]}
 --> c

1>

erlide in Eclipse

在 Help -> Install new software 裡面把 http://erlide.org/update 網址貼到安裝網址上,選擇安裝 Erlang IDE 與 Erlang add-ins。在 Window -> Preference -> Erlang -> Installed runtimes 裡面,可看到 erlide 有自動找到 erlang runtimes。

erlide 是個好用的開發 ide,但當我們把 eclipse 直接關閉時,在 eclipse 的 working directory 會出現一個檔名類似 rpc_monitor-20140108_155607.dump 的檔案,這個 dump 檔,有時後會出現 erl_crash.dump。

erlide 對錯誤訊息的列印還有些問題,一直看不到結果的時候,原因應該跟 No error message in Eclipse consloe/erang shell 一樣,因為 erlide 並不是直接跟 shell 互動,而是透過一層遠端呼叫,再呼叫 shell,這個問題到現在還沒解決。

erlide 好用,但以上面的問題來看,程式測試可能還是要回到 console 自己用 erl 測試,才會得到比較確切的回應結果。

調整開發環境

設定載入程式碼的搜尋路徑

  1. @spec code:get_patha(Dir) -> true | {error, bad_directory}
    將新目錄加入到載入路徑的頭
  2. @spec code:get_pathz(Dir) -> true | {error, bad_directory}
    將新目錄加入到載入路徑的尾
  3. @spec code:get_path() -> [string()]
    得知目前載入路徑
  4. @spec code:all_loaded()
    得知目前所有載入的模組
  5. @spec code:clash()

當系統開始時,執行一組命令 .erlang

在 home 目錄,放一個 .erlang 檔案,當 erl 啟動時,會先讀入此檔案,且執行裡面的內容。

如果是在 Windows ,則把檔案放在 c:\Users{username}\ 這個目錄中。

%% .erlang
io:format("Running Erlang~n").
code:add_patha(".").
code:add_pathz("/home/bin").

啟動 erl 時會看到

>erl
Running Erlang
Eshell V5.10.4  (abort with ^G)
1> code:get_path().
[".","c:/PROGRA~1/ERL510~1.4/lib/kernel-2.16.4/ebin",
 "c:/PROGRA~1/ERL510~1.4/lib/stdlib-1.19.4/ebin",
 "c:/PROGRA~1/ERL510~1.4/lib/xmerl-1.3.5/ebin",
 "c:/PROGRA~1/ERL510~1.4/lib/wx-1.1.1/ebin",
 "c:/PROGRA~1/ERL510~1.4/lib/webtool-0.8.9.2/ebin",
 [...]|...]

另一種方式,也可以在 erl 加上參數 -pa -pz ,將搜尋路徑加入到路徑的頭或尾

erl -pa Dir1 -pa Dir2 ... -pz DirK1 -pz DirK2

crash dump

如果 erlang 當機,會產生一個 erl_crash.dump 的檔案,如果要分析此 dump,有一個網頁版的 dump 分析器

>erl
Running Erlang
Eshell V5.10.4  (abort with ^G)
1> webtool:start().
WebTool is available at http://localhost:8888/
Or  http://127.0.0.1:8888/
{ok,<0.35.0>}
2>

然後在瀏覽器瀏覽網址 http://localhost:8888/ ,就可以啟動 CrashDumpViewer 上傳並分析 erl_crash.dump。

escript

escript 可直接執行用 erlang 寫的 script file,在 linux 環境,可以直接編輯一個執行檔

>vi hello
#!/usr/bin/env escript
main(_) ->
    io:format("Hello World.~n").
>chmod 755 hello
>./hello
Hello World.

在 Windows 環境

% file: hello.escript
main(_) ->
    io:format("Hello World.~n").

可直接在命令提示字元中,執行此 script

>escript hello.escript
Hello World.

參考

Erlang and OTP in Action
Programming Erlang: Software for a Concurrent World

2014/1/20

Why erlang? What can erlang do for us?

Erlang 是一種通用的平行導向程式語言,老爸是Joe Armstrong,想開發出一個共時、分散式、多核心、容錯的系統,Erlang 是個絕佳的選擇。

C10K Problem

著名的 The C10K problem 告訴我們,在高速網路與便宜的硬體的帶動下,如果一台機器能服務更多使用者,就代表著每一個客戶端消耗掉的運算資源更少,成本更低。

要解決這個問題,最重要的是要處理網路IO與硬碟IO,這些方法的區隔與不同之處如下:

  • 決定要不要在 single thread 中處理多個 I/O calls,要怎麼做?

    1. 不使用single thread處理多個 I/O 的方法,還是沿用 blocking/synchronous calls 並持續以 multiple threads or processes 來處理 concurrency 的問題

    2. 使用 nonblocking calls 啟動 I/O (e.g. write() on a socket set to O_NONBLOCK),並以有資料需要讀取的通知 readiness notification 來告訴該 channel 要啟動一輪新的 I/O (e.g. poll() or /dev/poll) 這種方法只適合 network I/O 不適合 disk I/O

    3. 使用 asynchronous calls 啟動 I/O (e.g. aio_write()),處理完成後發出通知(例如 signals or completion ports),這種方法同時適合 network 與 disk I/O。

  • 如何撰寫處理每個客戶端的程式碼?

    1. 每一個 client 使用一個 process (這是Unix 自 1980年開始,就一直採用的方法)

    2. 一個 OS-level thread 服務多個 clients,每一個 client 透過以下某個方式控制

      2.1 一個 user-level thread (例如 GNU state threads, classic Java with green threads)

      2.2 a state machine (a bit esoteric, but popular in some circles; my favorite)

      2.3 a continuation (a bit esoteric, but popular in some circles)

    3. 一個客戶端使用一個 OS-level thread (e.g. classic Java with native threads)

    4. 一個 active client 使用一個 OS-level thread (e.g. Tomcat with apache front end; NT completion ports; thread pools)

  • 不使用標準 OS services,直接將程式碼放進 kernel (e.g. in a custom driver, kernel module, or VxD)

下面的五種是最常見的組合方式

  1. 一個 thread 服務多個客戶端,使用 nonblocking I/O 和 level-triggered readiness notification
  2. 一個 thread 服務多個客戶端,使用 nonblocking I/O 和 readiness change notification
  3. 一個 thread 服務多個客戶端,使用 asynchronous I/O
  4. 一個 thread 服務一個客戶端,使用 blocking I/O
  5. 把程式碼編譯進 kernel

Currency in Erlang

共時導向編程 提到「共時」有兩種不同的作法,分別是「共享狀態共時」,與「訊息傳遞共時」。大多數的主流語言(Java、C#、C++)採用「共享狀態共時」的設計,但 Erlang 採用「訊息傳遞共時」。

在Erlang中沒有共享狀態,唯一一種交換資料的方式,是透過「非同步」訊息傳遞。Erlang的共時單位稱為行程(process),但其實是類似執行緒的地位,而不是真正的OS process。Erlang的行程之間彼此透過訊息傳遞方式來進行溝通,每個行程都可以在不同的核心,不同的CPU,甚至不同的電腦上執行。

對照到上面的 C10K 的解決方案,Erlang 的方法是一個 thread 服務一個客戶端,使用 asynchronous calls 啟動 I/O,處理完成後發出通知,從上面的分析,我們也知道這種方式,同時可以處理 網路 與 Disk I/O。

Java設計分佈式網絡架構的不足及與Erlang的比較 中提到,傳統的 RPC (Java RMI) 都是錯誤的,我們不應該想辦法把遠端呼叫包裝成跟 local call 一樣。Erlang 沒有 RPC,只有 message send/receive,receiver也能設定 timeout。

Dig into Erlang

雖然 Erlang 天生的分散式及語言本身的特性,讓他非常適合實作一個多工、容錯、分散式的系統,但並不是學會了 Erlang 的語法,了解了 OTP,就代表我們可以完美地解決 C10K Problem,甚至能處理到 C500K Problem,這牽涉到語言熟悉度,以及一些性能調整的技巧,換句話說,我們需要花時間深入 dig into the world of Erlang。

從 C10K 到 C500K 這篇文章說,現在大家開始在談 C500K,而不是 C10K 了,也提供了資訊(Linux Kernel Tuning for C500k),使用 Java+NIO 的方式,達成 C500K 的指標。

其實要達成 C500K,也不一定要用 Erlang,用 Java+NIO 也可以。那麼是不是就不要用 Erlang 呢?Erlang 還有其他優勢是 Java 欠缺的,那就是容錯與分散式。

當我們以 Erlang OTP 撰寫服務時,可以很快速,在不需要修改太多程式碼的狀況下,將程式放在多個 Erlang VM 中運作,而且這幾個 Erlang VM 可以在單一或多個實體機器上執行,Erlang OTP 也內建了monitor機制,可以設定 process 存在的條件,在發生錯誤時,自動重新啟動,或做對應的處理。這樣的優勢在 Java 開發上完全是看不見的。

世界是平行的

當我們搜尋 Erlang 時,在繁體中文的網頁中,一定會搜尋到蔡學鏞先生寫的 Erlang:世界是平行的!,還有他翻譯而且已經絕版的 Erlang程式設計 這本書。Erlang的相關資料除了英文,還有對岸 Erlang資源列表 產出了很多翻譯的資料,背後的原因,當然是經濟規模。

在台灣的軟體開發,要求要能處理 C1K 已經算是個大型的系統了,一般中小企業的系統,同時大概都是幾十到幾百個使用者使用而已,在大陸,一個城市的經濟規模的最大值就可能遠遠超過台灣的狀況。

但我們也不需要妄自菲薄,大陸的軟體公司,也不是每一個都有需要作到 C10K,也有很多中小企業需要的軟體,並不是每一個系統,都要杞人憂天,幻想會有超多的使用者同時連線進來使用。畢竟系統都是在一個進化的過程中,慢慢地因應需求,逐漸調整到最佳狀態。

erlang 的語言特色

  1. 平行導向程式設計: 以 spawn/* 函式,將函式設定為獨立的 process,後續使用訊息互相傳遞,進行跨行程通訊
  2. functional programming
  3. single assignment: 變數只能被設定一次,不能異動
  4. dynamic data type: 動態資料型別
  5. exception catching: 以 try catch 進行異常處理
  6. code hot deployment: 在不中斷系統的情況下,進行程式替換

LYME/LYCE

在 open source 軟體堆疊中,如果要建構一個支援動態網頁內容的網站,相對於常見的 LAMP:Linux, Apache HTTP Server, MySQL, PHP,在 Erlang 環境有著 LYME/LYCE:Linux, Yaws, Mnesia/CouchDB, Erlang 的軟體bundle。

要開發網頁程式,LYME 也能提供完整的開發環境,而且除了 Linux 沒什麼好挑的之外(其實可以選擇自己慣用的 distribution,我們是用 CentOS),網頁 Server Yaws也有替代選擇:Mochiweb、Misultin、Cowboy,資料庫 Mnesia 的替代選擇為:CouchDB、Riak、SimpleDB、Cloudant、Couchbase Server。

Prepare to Jump Higher

C1K 已經很夠用了,那我們選擇學習 Erlang 的用意在哪?就是為了要「準備」,公司目前雖然還是在小企業的規模中掙扎,一開始也不自我膨脹,引入外部資金來支撐一些線上服務的夢想。

我們是雙面人,一方面要激進地嘗試不同的領域與技術,另一方面又是保守的,就算是要燒錢,也要在更有把握的條件下孤注一擲。

接下來我們會嘗試從基本語法開始,認識 Erlang 的語言特性,然後是 OTP,最終目的是產出一個 open source 專案。

Erlang 的爸爸:Joe Armstrong

最後要認識一下 Erlang 的爸爸 Joe Armstrong,SICS首頁 有他的照片。1986年釋出了第一版的 Erlang,到今天已經過了28年。

Github:http://joearms.github.io/
SICS:http://www.sics.se/~joe/
Blog:http://armstrongonsoftware.blogspot.tw/ 已經很久沒有更新了)

2014/1/18

一位 pro leader 該有的條件

先前的文章我眼中的 pro programmer,討論到一位專業的程式設計師,該有什麼樣子,當時的文章最末段也想過,該找個人寫篇 pro leader 的模樣典範,而今天剛好看到InfoQ提供了一 篇文章的翻譯:優秀的領導與差勁的領導,原文是 Vlad Mihalcea 的一篇文章:Good vs Bad Leader,內容是討論一位專業的 leader 該是什麼樣子。

翻譯文章的內容雖然跟原文一樣,但是卻放棄了遵循原文表格對照的寫法,這降低了文章的可讀性。我並沒有得到原作者的同意,可以將整篇文章逐字翻譯,但我想我可以原作者的表格方式,然後嘗試用簡單幾句話做個整理。

context good leader bad leader
負責 承擔自己專案的成敗 永遠會怪別人
努力工作 以身作則,不管有趣或無聊的工作都做 不再寫程式
指導新人 注意新人的狀態,懂得在放手跟要求中間拿捏分寸 新人就是要操他才會進步
尊重 尊重每一個人 成員有人犯錯就落井下石
升遷 相信技術與專業會得到回報 沒料,只會逢迎拍馬、耍嘴皮子
信任 相信團隊成員,大家在互動中成長 不相信別人,技術差的只能做寫文件或做單元測試
任務分配 做大家不願意做的事 只會選自己喜歡的事做,困難的推給其他成員解決
問題回報 盡可能解決問題,也會及時向上回報狀態 掩飾問題,紙包不住火時就找替死鬼
代碼審查 相信 code review 的價值,重複發生的問題就寫到共享 blog 中 不做 code review,大家各自為政
挫折 吸收別人的經驗,提醒自己不重蹈覆轍 讓成員也經歷自己經歷的錯誤
新知 重視傾聽,接受 brain storming 對無聊的建言嗤之以鼻,偷取成員提出有趣的想法,往上回報。

關於挫折的部份,原作者覺得 bad leader 會讓成員也經歷自己經歷的錯誤,雖然 leader 該想辦法讓成員避開一些顯而易見的石頭,應該給新人一些必要的協助,但我認為人類有時候,就是會有「不經一事,不長一智」的狀況,沒有痛過就不懂得要如何成長,總有些時候,是其他人不管怎麼苦口婆心嘮嘮叨叨,可是當事者就是沒辦法體會別人好意的提醒的時候,這也只能讓事情發生,然後再回過頭來一起檢討。

原文把團隊成員都假設是個成熟的個體,能跟 leader 有著良好的互動,但世事並非一直都很如意,有時會遇到一些無法跟團隊融合的個體,發生這種狀況時,已經沒辦法討論誰對誰錯了,一個 leader 就該承擔起來,採取適當的行動,如果真的沒辦法解決,也只能壯士斷腕,避免問題擴散到其他成員身上,畢竟一個 team 一定會有個共用的習慣與潛規則,這是不容被質疑與打破的默契。

我認為自己針對團隊管理上需要下些決定時,心裡抱持著的最高指導原則是:「我希望大家都能發揮自己的優勢,逐日進步,以接受更多的挑戰。」挑戰不單指是更高超的技術,有時候是為了大家可能會遇到的下一個職業生涯階段需要做的準備與練習。這些動作會讓直接減輕我在一些專案上的 coding effort或是管理的 effort,感覺上像是我的工作內容減少了很多,其實我是把現在手上的工作交給適當的成員,然後設法包些其他的事情去做,有點像是一直擴大工作半徑的團隊同心圓的感覺。

因為我在專案中不是扮演實作的角色,而是在建立與定義商業邏輯與規格,分配工作,盯進度,確認成品的品質...等等。有幾次我以講笑話的方式脫口而出:「反正我也不用寫這些程式」,但其實講出來之後,卻又後悔覺得這樣講並不恰當,我不曉得有沒有人在意,「說者無心、聽者有意」。其實我有時也會想,能夠像以前一樣其實也很好,不講什麼話,專心coding,不用刻意注意每個成員的狀態,又能更深入熟悉基本的技能,不會停留在懂得皮毛的狀態。但我現在已經不能真的這樣做了,這樣反而有可能變成搶了成員表現自己的機會,也可能造成一些不信任感。

team leader 該要注意 member 的狀態,處理跟人有關的問題,也有技術上的基本條件,因為不懂技術,無法從根本上驅動團隊成員,也不知道怎麼拿捏評斷每一個人能力的標準,也無法適當地判斷並分配工作項目,否則也只是會造成團隊產出的成品,砸到自己的腳的狀況。