2008年6月28日

Moving to Beijing in Two Weeks

Not for the Olympic Games, I am leaving Taiwan for China because I've just accepted a job there. The CTO is an old friend of mine. He has been hearing me talking about REBOL for many years. Since REBOL Technologies will support Unicode in REBOL/View soon, there is a good chance that we'll use REBOL in our next product.

2008年6月20日

相等

欲判斷兩個值是否相等,利用「=」op!或者「equal?」action!,例如:

>> 1 = 1
== true
>> equal? 1 1
== true
>> "ABC" = "abc"
== true

即使型別不同,值也可能相等,例如:

>> 1 = 1.0
== true

上面的1是integer!,而1.0是decimal!,兩者型別不同。

如果要求嚴格一點,必須值和型別都一致,才能算相等,這時候使用「==」op!或「strict-equal?」action!。例如:

>> 1 == 1.0
== false
>> strict-equal? 1 1.0
== false

判斷兩個值是否不相等,利用「<>」op!或「!=」op!,或者「not-equal?」action!,例如:

>> 1 <> 2
== true
>> 1 != 2
== true
>> not-equal? 1 2
== true

判斷兩個值不相等「且」型別也不一致,利用「strict-not-equal?」action!(沒有對應的op!可用)。目前「strict-not-equal?」有bug。

=?用來判斷兩個參考(reference)是否一樣(identical),只有用在function, Series, object等參考型別,才有意義。用在其他型別時,相當於「=」。「=?」如果一邊是參考型別,另一邊是其他型別(非參考),則一定為false。

2008年6月19日

閱讀REBOL源碼時的干擾

我發現在閱讀REBOL源碼時,如果參數或修飾字(refinement)所使用的名稱和REBOL常用函數同名時,會造成我相當大的閱讀困擾。例如:

replace: make function! [[
target [series!]
search
replace
/all
/case
/tail
/local save-target len pos value do-break
][
....
]

這裡出現的replace,除了是函數名稱,也是參數;all、case、tail除了是修飾字,更是很常用的REBOL函數。我只要忘了它們是修飾字和參數,一直以函數的方式對待它們,就會造成閱讀源碼時理解力受到干擾。例如:

do-break: unless all [:break]

我一直覺得這裡的程式有錯,一直到很久以後才發現這裡的all不是函數,而是修飾字。

寫這篇blog文章,用來提醒自己和本blog的讀者(if any),閱讀REBOL源碼時,一定要注意這種狀況。

In REBOL 3.0, op! is infix only.

剛剛發現,在REBOL 3.0中,op!居然「只能」進行中序(indix)運算,這和REBOL 2.x的作法不同。
R2 >> 1 + 2
== 3
R2 >> + 1 2
== 3

R3 >> 1 + 2
== 3
R3 >> + 1 2
** Script error: + operator is missing an argument

我認為R3的作法比較好,可以簡化解譯器。以後op!應該不會再支援prefix。

My 8 REBOL Articles Published in China, 2004

我曾經在中國大陸的雜誌發表過一系列REBOL文章(約8篇),本來考慮要在這個Blog重新發表這系列文章,但是最近重新看了一下,許多內容其實已經過時,和REBOL 3.0的現況以及REBOL未來的走向不符合。所以這些文章只好石沈大海了!

如果你手邊還有我的這些文章,其實你不需要花時間閱讀它們。把時間花在REBOL 3.0版,這才是未來,這才重要。

Bug: change/part

軟體開發就是這樣,有時候消除掉十個Bug,卻會因此導致一個原本不存在的Bug。剛剛發現,原本沒問題的函數Change,現在卻有了一個Bug。

>> data: "C# Rules"
== "C# Rules"

>> change/part data "REBOL" 2
== "les"

>> data
== "REBOLles"

由於Change是Action!,所以我無法fix。我已經將它呈遞到Bug Database了。

Bugfix: script?

修改後的結果如下:

script?: make function! [[
{檢查是否具有有效的劇本標頭(暫時)。}
source [file! url! binary! string!]
][
switch type?/word source [
file! [ source: read source ]
url! [ source: read source ]
string! [ source: to-binary source]
]
find-script source
]]

A Secret Project Is Going On

根據Carl部落格文章「A Busy String 2008」的說明,今年夏天較大規模釋出REBOL 3.0的目標不變。除此之外,他還提到REBOL 3.0會有一個神秘的內建功能,至於是什麼,目前他先賣個關子。

我一向認為「謠言始於智者」,我開始散播謠言。我猜想應該是「軟體線上市集」或「AltME傳訊軟體」或「Peer-to-Peer軟體」之類的。

2008年6月18日

小心TO-BLOCK的陷阱

許多REBOL函數的參數都同時接受單一值,或多重值。當然多重值會放在block中。我們經常會需要在函數內,先將某參數化成block,然後才進行處理,這時候程式會這麼寫。

if not block? :args [args: reduce [:args]]

透過to-block(或者to block!),可以簡化上面的作法,改寫成這樣:

args: to-block :args

to-block會判斷,如果後面的參數是any-block!,則不處理,直接傳出其block形式;如果參數不是any-block!,則傳出以該參數為「唯一」元素的block。如下所示:

>> to-block 1
== [ 1 ]
>> to-block [ 1 ]
== [ 1 ]

To-block確實相當方便,但是要注意一個特殊狀況,那就是any-block!除了block!之外,還包括其他:

>> ? any-block!
ANY-BLOCK! is a typeset of value: make typeset! [block! paren! map! path! set-pa
th! get-path! lit-path! ]

因此下面的狀況固然可能非你所願,但卻是正確的:

>> to-block 'system/version
== [ system version ]

這個時候,只好回去改用一開始的寫法:

if not block? :args [args: reduce [:args]]

Bugfix: ??


??: make function! [[
{印出變數名稱,後面跟著它的鑄模值(幫助除錯用)。}
'name
][
print either any [word? :name path? name] [
repend form name [": " mold name: get :name]
] [
mold :name
]
:name
]]

上面是??函數的源碼,有個小小的bug。必須把其中的form改成mold,否則當參數是path的時候,「/」會消失,例如:

>> ?? system/version
system version: 2.100.13.3.1
== 2.100.13.3.1

另外「any [word? :name path? name]」也應該要改成「any [word? name path? name]」。

R2 vs. R3 Contexts

Brian Hawley是頂尖的REBOL專家之一,他對於程式語言的理論於實務,以及REBOL內部的運作,都相當熟悉。我經常在與他交談的過程中,學到相當多知識。他有一篇關於REBOL Context的說明,相當精采。文章請見 http://www.rebol.net/wiki/R2_vs._R3_Contexts

2008年6月17日

Cheyenne Web Server

看到Cheyenne(http://cheyenne-server.org/),你一定會感到很驚訝…一個具有實用功能的Web Server系統,居然如此小巧!Windows版本的exe檔,不到500 KB,這主要是因為,Cheyenne是用REBOL開發出來的。

Cheyenne作者Dockimbel表示,在一般雙核心的伺服器上,可以同時服務500個請求,Cheyenne適合用來架設中小型網站,也相當適合用來架設個人網站。目前Cheyenne雖然仍在Beta階段,但是已經相當穩定,也在某些網站中實際運作一段時日。

Cheyenne對於Web標準的支援已經算很完備,且持續在進步中。你可以在Cheyenne網站的首頁看到它目前所支援的標準,也可以在此下載它的REBOL源碼(BSD授權方式)或執行檔(Windows、Linux、Mac OS)。請注意,如果你要透過源碼執行Cheyenne,請使用REBOL 2.x版,而非REBOL 3.0版。

2008年6月16日

PICK is not picky

PICK可能是最不挑剔的(picky)REBOL函數,它接受的第一個參數可以是35種資料型別:

series! gob! pair! event! money! date! time! object! port! tuple ! bitset! any-function!

只有12個?別忘了,其中series!和any-function!是typeset!,將它們兩個展開之後變成

string! binary! file! email! url! tag! issue! bitset! vector! image! block! paren! map! path! set-path! get-path! lit-path! gob! pair! event! money! date! time! object! port! tuple ! bitset! native! action! routine! rebcode! op! closure! function!

共有35個資料型別。其中any-function!和object應該已經可以利用reflect(以及相關函數)取代,只是目前REBOL 3.0尚未禁止使用REBOL 2.x的pick、first等函數進行reflect。

另外有兩個可疑的資料型別,分別是event!與money!,我認為PICK不應該用來處理它們。我已經報告到Bug資料庫了。

閱讀REBOL源碼

我記得十多年前學習Borland OWL時,由於參考資料不多,我必須閱讀源碼才能獲得更進一步的資訊。當時意外的收穫是,我的C++功力大增。

當參考資料不足時,我們就必須閱讀源碼。最近我開始感覺到有閱讀REBOL源碼的必要,因為REBOL 3.0正在開發階段,大家不是很有空去寫DocBase的文件,而DocBase的文件我已經看得差不多了。

除了action和native之外,其實REBOL將所有的源碼都公開。你可以利用source函數或probe函數取得源碼。或者用下面的方式,將全部的源碼都輸出到一個檔案(例如C:\R-Source.txt):

write %/c/R-Source.txt to-binary mold system

請在「完全乾淨」的REBOL console在做上面的動作,也就是說,開始一個新的REBOL console,然後馬上做這個動作。如果你先執行過一些敘述,可能會造成你的系統環境受到污損(contaminated)。

這個檔案雖然不小,但是其中有相當多重複的地方。例如system/contexts/system、system/contexts/export、system/contexts/current、system/words是完全一樣的內容,將重複的內容刪除之後,你會發現檔案變小許多。

然後你就可以開始閱讀REBOL的源碼了。閱讀大師寫的源碼,有助於自己未來成為大師。

2008年6月15日

China Developing Interest in REBOL

According to Axlea, for the past week, China seems very interested in REBOL.

  • REBOL.COM ( 6.0 % -> 7.5 % )
  • REBOL.NET ( ? -> 16.8 % )
  • REBOL.ORG (10.2 % -> 13.6 % )

Wow, not bad.

不使用VID,也可以寫出GUI程式

REBOL有意打造VID(Visual Interface Dialect),成為適合大多數人使用的GUI程式設計方式。從許多Carl的Blog文章中,以及Gabriele(負責實踐與設計VID的人)目前的設計成果,我深切相信這樣的目標確實可以達成。

一方面基於想要「自我挑戰」,另一方面當作學習REBOL 3.0過程的歷練,今年一、二月的時候,我不使用VID,而直接使用GOB和View,寫過數十KB的GUI程式。這樣的經驗讓我體會到:「如果不使用VID,設計GUI會很辛苦」,所以對大多數的人來說,我並不建議這麼做。

但我還是準備自己從頭打造自己的GUI,主要是因為,我希望我的GUI能很動態(dynamic),且支援雙向文字(bi-directional text)。我已經花了相當多時間進行相關的Layout Manager程式編寫與除錯,也獲得一些基本的成果,但是最近因為工作的關係,這件任務可能必須拖延到今年第四季,我才有時間繼續。但是在這之前,我會持續加強我自己的REBOL 3.0程式能力,Prepare for prime time。

2008年6月14日

map、remove-each、foreach

許多Functional Language(例如Erlang與F#)都具有很方便的map、filter、iter函數,可以將一個List,對應到另一個List。在REBOL 3.0中,也有類似的函數,分別是map、remove-each、foreach。比較大的差異是,其他語言是利用Higher-Order Function做到的,但REBOL是用Block做到的。

Event Handler

Event Handler是一種物件,具有三個word,分別是name、priority、handler,如下面的範例所示:
make object! [
name: 'view-default
priority: 50
handler: func [event] [
print ["view-event:" event/type event/offset]
switch event/type [
close [quit]
key [if event/key = escape [quit]]
]
show event/window
none ; we handled it
]
]

一般我們會在View函數中,利用下面的方式指定Event Handler:
view/options window [
handler: [
name: 'my-handler
priority: 100
handler: func [event] [
if event/type = 'down [quit]
]
]
]

注意,在上面的例子中,我們並未建立物件(make object!),而是以區塊(block!)的方式指定此Event Handler的內容。

在REBOL/View系統中,可以同時具有多個Event Handler,將它們串接起來。你可以透過handle-events與unhandle-events來分別增加與刪除Event Handler。注意,handle-events需要的參數是block!,而unhandle-events需要的參數則是物件。

每個Event Handler都具有一個name(名稱),是用來識別Event Handler之用的。想知道目前有沒有具備某名稱的Event Handler,可以透過handled-events?函數。此函數需要傳入一個名稱,它會傳出此名稱的Event Handler(如果有的話)。

利用handle-events函數加入一個Event Handler時,會根據其priority(優先權),決定其位置,優先權越高者放在越前面。例如,現在已經有兩個Event Handler,分別為優先權500的A與優先權200的B,再新增一個優先權為300的C時,最後的結果會是A、C、B,因為C的優先權介於A、B之間。

每個Event Handler都具有一個handler,這是真正處理事件的函數,所以需要事件當參數。特別注意的是,handler的傳出值如果為event,則處理完目前的Event-Handler之後,會繼續處理下一個Event Handler;handler的傳出值如果為none,則表示此事件已經處理完畢(被消耗掉了),不用再往後面的Event Handler遞送。例如,目前依序有A、C、B這三個Event Handler,其中A的handler函數傳出值為none,則C與B都不會發生作用。

2008年6月13日

MAP-INNER and MAP-OUTER are killing me

我一直搞不懂MAP-INNER與MAP-OUTER要如何使用,也沒有文件說明可以閱讀。我在動手寫程式做了一些實驗之後,終於放棄了。我於是定義了下面的函數(取得某個gob的絕對座標):
abs-offset?: func [ gob [gob!] ] [
negate second map-inner gob 0x0
]

以後我打算只使用MAP-EVENT和abs-offset?,不再碰觸令人痛苦不已的MAP-INNER與MAP-OUTER。

MAP-EVENT的用法


REBOL 3.0最近新增一個函數MAP-EVENT,相當好用,可以將滑鼠相關的event,從「最底層的視窗gob」對應到「最上層的gob」。新產生出來的event,其offset與gob屬性都會被修改。通常用法會是這樣:

event: map-event event

假設滑鼠是圖中的黑點,則對應前event/gob會得到紅色的gob,且event/offset會得到黑點和紅色gob左上角的相對位置。對應後,event/gob會得到藍色的gob,且event/offset會得到黑點和藍色gob左上角的相對位置。

Bugfix: parse-url

REBOL 3.0目前剖析URL的時候有Bug,當Path出現「@」、「!」或中文、日文、韓文…的時候,Path的解析出錯。這是相當嚴重的問題,因為檔案名稱中很常出現各國語言,如果用URL表示檔案,就會出問題,例如「file:///C:/音樂/love.mp3」就無法被順利剖析。

剖析URL的函數是decode-url,事實上,此函數會去使用到system/intrinsic/parse-url物件的定義。在parse-url物件定義中,為了要讓path納入「@」與「!」,必須修改path-char的定義修改如下:

path-char: make bitset!
#{00000000EFFFFFF5FFFFFFF57FFFFFEA}

為了要納入各國語言(#"^(100)" - #"^(FFFF)"),更必須將它改成:

path-char: union
make bitset!
#{00000000EFFFFFF5FFFFFFF57FFFFFEA}
charset [#"^(100)" - #"^(FFFF)"]

我改天再把它呈遞給DevBase。

FORMAT與PRINTF的用法

REBOL 3.0提供format與printf函數,這兩個函數的參數完全一樣。Format函數會傳出格式化的字串,而printf則會印出格式化的字串。事實上,printf內部直接呼叫format。

>> source printf
printf: make function! [[
"Formatted print."
fmt "Format"
val "Value or block of values"
][
print format :fmt :val
]]
>>

下面是它們的使用範例:

>> format [ 20 #"/" -10 ] [ %my-rebol-file.r 31023 ]
== "my-rebol-file.r / 31023"

>> printf [ 20 #"/" -10 ] [ %my-rebol-file.r 31023 ]
my-rebol-file.r / 31023
>>

printf與format都需要兩個參數,第一個是格式規則,第二個是代換內容的區塊(block)。格式規則區塊內可以出現四種元素:
  • 正整數:指定內容長度,如果內容超過指定長度,則自動放寬。如果內容不滿指定長度,則「補上空白」,且內容「向左對齊」。
  • 負整數:指定內容長度,如果內容超過指定長度,則自動放寬。如果內容不滿指定長度,則「補上空白」,且內容「向右對齊」。
  • 字元:直接放置一個字元
  • 字串:直接放置此字串

使用format時,還可以透過/pad,指定補上某字元(或字串),而不是補上空白。例如:



>> format/pad [ 20 #"/" -10 ] [ %my-rebol-file.r 31023 ] #"."
== "my-rebol-file.r...../.....31023"

Bugfix: ls與format

我已經修正完畢LS函數和FORMAT函數的Bug,下面列出的是FORMAT的定義。至於LS的定義修改方式很簡單,把LENGTH?用LENGTH+?取代即可。當FORMAT遇到中文時,終於不會出問題了!

LENGTH+?不會計算出字串的字元長度,而是會計算出「相當於多少個half-width字元」的長度。記得把前一篇文章的Full-Wide-Width-charset定義也加進來,因為LENGTH+?會用到這個定義。

我會找一天把修正後的結果透過DevBase遞交給REBOL公司。

length+?: func [str [string!] /local ans ] [
ans: 0
foreach ch str [
either find Full-Wide-Width-charset ch [
ans: ans + 2
] [
ans: ans + 1
]
]
]

format: make function! [[
"Format a string according to the format dialect."
rules {A block in the format dialect. E.g. [10 -10 #"-" 4]}
values
/pad p
/local out val
][
p: any [p #" "]
unless block? :rules [rules: reduce [:rules]]
unless block? :values [values: reduce [:values]]
out: make string! ""
foreach rule rules [
if word? :rule [rule: get rule]
switch type?/word :rule [
integer! [
val: first+ values
val: form :val
pad: (abs rule) - length+? val
if negative? pad [ pad: 0 ]
either positive? rule [
append out :val
append/dup out p pad
] [
append/dup out p pad
append out :val
]
]
string! [append out rule]
char! [append out rule]
]
]
if not tail? values [append out values]
head out
]]

Format函數與寬字元

REBOL 3.0有提供一個函數名為format,用來進行輸出文字的格式化。許多函數都會用到format,例如what、ls。

format一遇到中文就會失靈,輸出格式亂掉,這是因為中文字是寬字元(Wide Character)。為了要修正format的此錯誤,我剛剛把Unicode Standard Annex #11 (East Asian Width) 所附的檔案整理過,找出其中屬於Full-Width與Wide的字元。接下來,就等我(或任何人)有空去修正format函數的定義了。
full-wide-width-charset: charset [
#"^(1100)" - #"^(1159)"
#"^(115F)"
#"^(2329)" - #"^(232A)"
#"^(2E80)" - #"^(2E99)"
#"^(2E9B)" - #"^(2EF3)"
#"^(2F00)" - #"^(2FD5)"
#"^(2FF0)" - #"^(2FFB)"
#"^(3000)" - #"^(303E)"
#"^(3041)" - #"^(3096)"
#"^(3099)" - #"^(30FF)"
#"^(3105)" - #"^(312D)"
#"^(3131)" - #"^(318E)"
#"^(3190)" - #"^(31B7)"
#"^(31C0)" - #"^(31E3)"
#"^(31F0)" - #"^(321E)"
#"^(3220)" - #"^(3243)"
#"^(3250)" - #"^(32FE)"
#"^(3300)" - #"^(33FF)"
#"^(3400)" - #"^(4DB5)"
#"^(4E00)" - #"^(9FC3)"
#"^(A000)" - #"^(A48C)"
#"^(A490)" - #"^(A4C6)"
#"^(AC00)" - #"^(D7A3)"
#"^(F900)" - #"^(FA2D)"
#"^(FA30)" - #"^(FA6A)"
#"^(FA70)" - #"^(FAD9)"
#"^(FE10)" - #"^(FE19)"
#"^(FE30)" - #"^(FE52)"
#"^(FE54)" - #"^(FE66)"
#"^(FE68)"
#"^(FF01)" - #"^(FF60)"
#"^(FFE0)" - #"^(FFE6)"
]

2008年6月12日

對Object!進行Append與Length?

REBOL 3.0現在允許擴充(extend)物件(object!),如下所示:
>> obj: context [ a: 1 ]
== make object! [
a: 1
]
>> append obj [ b 2 c 4]
== make object! [
a: 1
b: 2
c: 4
]

上面的例子中,[ b 2 c 4] 也可以寫成 [ b: 2 c: 4],效果一樣。

如果Block內的word和Object內的word相同時,會造成修改的效果。例如:
>> append obj [c 3]
== make object! [
a: 1
b: 2
c: 3
]

上面的例子,由於obj本來就有c,所以append之後,c的值會被改變。

特別注意,這些處理series的函數中,只有append可以用在物件上,remove、insert並不適用於物件。我猜想可能是因為remove、insert會讓context內部binding對應的方式亂掉。

Block不會先被reduce,如果有reduce的必要,必須自己來。這個時候,通常會使用compose或reduce/no-set,例如:
>> append obj reduce/no-set [ d: 365 + now ]
== make object! [
a: 1
b: 2
c: 3
d: 12-Jun-2009/22:56:29+8:00
]

想知道某物件有多少Word,可以利用Length?,例如:

>> length? obj
== 4

效果相當於「length? words-of obj」。

2008年6月11日

My REBOL 3.0 Wishlist

  1. More text encoders/decoders (Big5, GB2312, ... )
  2. Unicode text rendering
  3. Unicode text input from keyboard
  4. Plug-in ( for calling native C functions )

Because I am developing:

  1. A spider program, which needs item 1
  2. A Chinese text editor (Transma), which needs items 1, 2, and 3
  3. An user interface for an utility, which needs items 1, 2, 3, and 4

2008年6月9日

Who is visiting REBOL.COM?

According to Alexa, REBOL.COM users come from these countries:

  • Germany 23.1%
  • United States 21.6%
  • India 14.3%
  • Canada 8.1%
  • China 6.0%
  • France 1.9%

Also, REBOL.NET users come from these countries:

  • United States 37.9%
  • Germany 19.2%
  • Malaysia 1.7%

And, REBOL.ORG users come from these countries:

  • United States 33.1%
  • Germany 16.7%
  • Iran 11.9%
  • China 10.2%
  • Canada 6.1%
  • France 1.7%
  • Philippines 0.7%

OOP in REBOL 3.0: An Example

REBOL[]


Inherit: func [super [object!] sub [object!]] [
append sub reduce/no-set [parent: super]
]


Invoke: func [ obj member /local ans] [
either ans: select obj member [
ans
] [
attempt [Invoke obj/parent member]
]
]


Super-Class: context [
Member-A: does [ print "Member-A of Super" ]
Member-B: does [ print "Member-B of Super" ]
]


Sub-Class: context [
Inherit Super-Class Self


Member-B: does [ print "Member-B of Sub" ]
Member-C: does [ print "Member-C of Sub" ]
]


Invoke sub-class 'Member-A
Invoke sub-class 'Member-B
Invoke sub-class 'Member-C

2008年6月8日

TCP/IP Client/Server的互動方式



透過「如何用REBOL寫TCP/IP的Client程式」與「如何用REBOL寫TCP/IP的Server程式」,你應該已經可以使用REBOL 3.0寫出TCP/IP的Client/Server系統。這裡是一個綜合的示意圖,展示出DNS、Client Port、Server Listen Port、Server Service Port之間的互動關係。

The Long Tail ?

我在BlogSpot開了兩個Blog,一個是「JerryLovesRebol.BlogSpot.com」,另一個是「RebolLovesJerry.BlogSpot.com」;前者是「言程序」Blog,內容是主流技術與前瞻技術的心得文章;後者是「Rebol This Rebol That」Blog,內容是和REBOL相關的消息與技術心得。我預期,REBOL是比較冷門的技術,所以雖然成立REBOL文章專屬的Blog,我並不預期會有太多人來看。沒人來閱讀我的REBOL文章是無所謂的,就當成是自己的學習筆記。

但結果令我意外,「言程序」的點擊率居然還沒有「Rebol This Rebol That」高。我分析一下原因,可能是「長尾效應」(Long Tail)。

「言程序」Blog大多數的文章其實已經先發表於台灣的iThome週刊與網站,所以大家不見得要到我的Blog來閱讀,而「言程序」的讀者只限於華人,所以算是區域性的Blog。

但「Rebol This Rebol That」Blog就不同了。這裡的文章並未發表於他處,且對REBOL技術感興趣的人「比例」雖然不高,但是全球累積起來的「數量」仍不少。這就是Long Tail!

除非懂中文,否則大多數的人閱讀「Rebol This Rebol That」Blog的時候,會透過「Google Translate」,先翻譯成英文。看他們這麼克難地閱讀,我有一點想用英文寫Blog。但我終究還是沒這麼做,因為顧慮到「Rebol This Rebol That」有一部份的任務是向華人介紹這個優秀的技術,既然如此,還是用中文寫比較恰當。

希望更多人用各種語言(捷克文、法文…)開闢REBOL blog,讓我們一起為REBOL的Promotion努力。

2008年6月7日

如何用REBOL寫TCP/IP的Server程式


在「如何用REBOL寫TCP/IP的Client程式」一文中,我說明了Client程式的寫法,現在這篇文章說明Server程式的寫法,程式碼請參考這篇DocBase文章的Pong Server。重點整理如下:

  1. Server會先Open自己電腦上的一個Listen Port,並開始聆聽(Listen)連線請求。對於REBOL來說,只要呼叫Open函數來開啟伺服器的Port,就會同時進行Listen(你不需要呼叫Listen,也沒有這樣的函數可用)。
  2. 當Client呼叫Open,並指定連到Server的IP和Port時,Server就會收到Accept的事件。處理Accept事件的方式是呼叫First,會得到一個Service Port。Server的Awake函數必須傳出false(表示任務尚未完成),否則會結束Listen,只能服務一人。
  3. Listen Port繼續接受來自其他Client的新連線請求,而Service Port開始和連線上的Client溝通。也就是說,Listen Port和Service Port兩個是不同的Port,且Listen Port只有一個,且可能產生出多個Service Port。
  4. Service Port會進行Read、Write、Close動作(函數),會分別收到Read、Wrote、Close事件。這部分和「如何用REBOL寫TCP/IP的Client程式」一文類似,不再贅述。
  5. 通常Service Port會先呼叫Read(而非Write),以讀取來自客戶的請求內容。

A New VID Prototype – VID 3.4

目前REBOL 3.0的開發焦點在VID。REBOL語言設計者Carl最近在AltME r3-alpha社群討論區表示,為了讓VID具有相當大的客製化(customization)彈性,將對既有的VID雛形系統(Prototype)做出較多深入的改變,然後推出第四個雛形系統(VID 3.4)。

Functional Programming

I've just published a series of FP-related articles in my non-REBOL blog.

Functional Programming (1) Language is functional again.
Functional Programming (2) Why Functional Programming.
Functional Programming (3) FP is as FP does

2008年6月6日

DevBase Is Around the Corner

剛剛釋出REBOL 3.0 Alpha 2.100.13,這個版本所做的改變,大多是為了VID和REBOL/Services的需求,而VID和REBOL/Services是DevBase的基石。DevBase是一個Client-Server的系統,Client的使用者介面會用到VID,Client和Server的溝通會用到REBOL/Services。

為了要擴大社群對REBOL開發的貢獻,在REBOL 3.0中,DevBase會擔負起這方面的角色,讓社群可以檢視REBOL的程式碼,修改,呈遞,最後由關鍵的兩三個人決定採用與否。目前DevBase已經在運行中,但是是透過REBOL 2.x。最新釋出的REBOL 3.0 Aplah上頭,也開發了一套DevBase,將會試圖取代REBOL 2.x的DevBase。

REBOL大將Gabriele在AltME上面的r3-alpha社群中提到,經他測試,REBOL 3.0的DevBase已經可以順利運作。我相信,這個月(說不定下週)如果他們發現DevBase一切都穩定的話,就會釋出這個DevBase版本。

GUI Made Easy

許多學術領域的專家或教授,會使用編程語言,將自己的研究結果,寫成特殊領域的程式,例如氣象、水利、天文、自然語處理...等。但是這些程式往往只有文字的輸入和輸出。他們很希望讓程式具有美觀的GUI介面,但是這部分他們做不來,因為「大多數的GUI程式設計都很複雜」。不信的話,你可以看看MFC、WinForms、WPF、Tk、Java Swing...,沒有一個是簡單的。對這些專家來說,「沒有GUI」是他們程式的一大缺失。

REBOL 3.0試圖利用VID(Visual Interface Dialect)做出相當容易寫的GUI程式,讓任何人都可以輕易地寫出GUI程式。如果你用過REBOL 2.x的VID,你應該已經體會到VID的方便。而在REBOL 3.0中,VID會更美觀、更有彈性、且支援Unicode。你可以閱讀Carl的三篇Blog文章:

2008年6月5日

Pathologic Obesity in Software

許多人第一次看到REBOL,都會為它小巧的體積感到相當驚訝,畢竟在軟體產業,肥胖已經成了一種通病。幾個月前,我發表了一篇「Pathologic Obesity in Software」(軟體的病態性肥胖),得到不少讀者的共鳴。你可以到這裡閱讀這篇文章。

OOP in REBOL ?

根據REBOL語言設計者Carl在部落格文章中的說法,他以前曾經相當著迷於OOP (Object-Oriented Programming),但是現在已經不再是如此了。雖然Carl不是特別喜歡OOP,但是在REBOL 3.0的設計中,我們依然可以找到少數OOP的影子。最明顯的例子是Scheme與Port。

從OOP的角度來看,Scheme就像是Class,Port就像是Instance;Scheme只有一份,被所有的Port共用。另外,Scheme也像是Super-class;而Port像是Sub-class,Port所提供的action方法可以覆蓋(shadow)掉Scheme的action;而Port如果沒有實做(implement)自己的action,就會使用Scheme的版本。

如果你想在REBOL中寫OOP的程式,那麼你可以參考Scheme與Port的源碼。關於OOP,你可以參考我的這篇文章

如何用REBOL寫TCP/IP的Client程式


我還記得約14年前讀大學時寫過的第一個網路程式WinBrick(Windows 3.1的網路對打俄羅斯方塊Tetris),當時使用WinSock API。後來讀研究所時用Java Networking API寫VOD(Video-On-Demand)系統,Java的API比C的WinSock稍微簡單一些,但程式還是不好寫。REBOL的TCP/IP程式設計方式,比起上述兩者,都更簡單。你可以閱讀Carl的這篇部落格文章
經過自己手動實驗之後,我簡單地整理一下重點:
  1. DECODE-URL用來將URL(例如:http://www.rebol.com/index.htm)拆解成區塊(例如[scheme: 'http host: "www.rebol.com" port-id: 80 path: "/index.htm"]),但是DECODE-URL有時候會拆解錯誤,如果與到拆解錯誤的狀況,就必須自己處理。如這個例子所示。
  2. 上述的區塊可以當作OPEN的參數,就可以連接到伺服器。
  3. 如果host是host-name,則程式會先連到DNS,查詢(lookup)伺服器的ip,所以會收到「lookup」事件,這個時候,必須再次呼叫OPEN。
  4. 如果host本來就是host-ip,則不會有上述的步驟(不會收到lookup事件)。
  5. 當程式收到「connect」事件時,表示程式對伺服器的連線請求,已經被伺服器接受(accept),可以開始進行資料通訊,這個時候,通常是由Client先傳送資料(服務請求)給伺服器,而伺服器先聆聽(listen)。因此,在收到connect事件時,Client通常會呼叫WRITE。
  6. 當資料寫入完畢,Client會收到「wrote」事件。這個時候,通常會呼叫READ,進行資料的讀取。當然,你也可以會直接呼叫CLOSE,主動關閉網路連線,這由你的通訊協定(protocol)決定。
  7. 呼叫READ,就會從緩衝區(buffer)讀取資料,一旦讀取完目前緩衝區的資料,就會收到read事件(這個read是「過去分詞」,表示「已經讀取了」)。
  8. 通常你需要解析剛剛讀到的資料,以瞭解資料是否已經讀完。如果尚未讀完,你必須繼續呼叫READ來繼續讀取資料。如果已經讀完,你可能會呼叫WRITE傳資料給伺服器,或者呼叫CLOSE來關閉連線。
  9. 當連線的兩端中,任何一端先呼叫CLOSE來關閉連線時,Client都會收到「close」事件。當client收到這個事件的時候,要做一些收尾的動作。
  10. awake的傳出值如果為false,表示port尚未處理完畢;如果為true,表示port處理完畢,會立刻結束這個port。通常我們會在遇到通訊協定格式錯誤、事件無法識別、或收到close事件之後,傳出true,其他狀況則傳出false。
  11. 為了避免任何動作做太久,我們通常會在外部呼叫WAIT,等候一段Timeout的時間。

2008年6月4日

我對REBOL 3.0的體會

我注意REBOL 3.0的開發過程,算是種「敏捷開發」(Agile Process),隨時都在進行動態的調整。如果開發者很多的話,這樣的敏捷開發恐怕不可行,一定要按部就班進行。最後的結果可能就是得到一個功能普通、體積肥大、且效能不佳的軟體。

由於REBOL 3.0的核心開發人力是Carl本人,所以他可以隨時動態調整開發項目的優先次序。例如原本Unicode要在3.1才加入,後來提前。這樣的例子屢見不鮮。

Carl在開發的過程,有一些理念上的堅持,那就是REBOL必須「簡單」、「體積小」、「效能」。任何作法只要違反這幾點,都會被排除,不予採納。所以即使REBOL已經開發這麼久了,且圖形和網路的功能都加進來了,還是只有約600K。且執行效率相當好,語法也維持一貫的簡單。

在這個年頭,軟體要保持「簡單」、「體積小」、「效能」,談何容易,但REBOL 3.0做到了。加上強大與創新的功能,這真的是一個好語言。

2008年6月3日

REBOL 3.0的進度


如果你關心REBOL 3.0現在的開發進度,你可以看一下這張示意圖。這是我個人認知的進度狀況,並非官方的資料,所以不保證精確。

2008年6月2日

暫時擱置Transma計畫

想用REBOL 3.0,繼續動手完成我之前的計畫(Transma),無奈REBOL 3.0尚未支援下面的功能:

  1. 從鍵盤取得Unicode輸入
  2. 將Unicode文字輸出到GUI
  3. 呼叫外部DLL

看樣子我只好持續把這個計畫擱置下來。