Saturday, June 24, 2006

Erlang + Yaws 101 ตอน 2

วันนี้ลองพูดถึงการ render output ของ yaws บ้าง
นามสกุลของ file ที่ yaws มองว่าเป็น dynamic script
ก็คือ .yaws

หน้าตาก็ประมาณนี้
<html>
<h1>Test</h1>
<erl>
out(A) ->
{html, "helloworld"}.
</erl>
</html>

โดยข้อกำหนดของ Yaws ก็คือ ภายใน tag erl
จะต้องมี function ที่ชื่อ out/1 ประกาศอยู่
หลายคนคงจะสงสัย ทำไมที่ท้ายชื่อมีเลข 1 แปะอยู่ด้วย
เนื่องจาก function ของ erlang ใช้กลวิธี pattern matching
ในการเลือก function ขึ้นมาทำงาน
ดังนั้น signature ของ function ก็เลยใช้ตัวเลขบอกจำนวน argument มาใช้

จะเห็นว่าหน้าตามันก็คือ template language ที่เราเขียนกันอยู่นี่แหล่ะ
ไม่่ว่าจะเป็น jsp, php, rhtml, ...

แต่บางคนไม่นิยมเขียนแบบนั้น
Style แบบนี้บางทีก็ดูถูกใจพวก hardcore มากกว่า
<html>
<erl>
out(A) ->
{ehtml, {table, [{bgcolor, grey}],
[
{tr, [],
[
{td, [], "1"},
{td, [], "2"}
]}]}}.
</erl>
</html>


ถ้าอยากได้ content-type แบบอื่นๆ ก็ให้ return แบบนี้แทน
<erl>
out(A) ->
{content, "text/json", "{name: 'hello'}"}.
</erl>


ในกรณีที่แปลงเป็น json ก็สามารถ download library ejson มาใช้ได้
<erl>
out(A) ->
{content, "text/json", json:encode(json:obj_from_list([{"foo", "bar"}]))}.
</erl>


ถ้าจะ redirect ก็ return แบบนี้
<erl>
out(A) ->
{redirect, "http://localhost"}.
</erl>

Related link from Roti

Thursday, June 22, 2006

Erlang + Yaws 101 ตอน 1

ลองมาเข้าใจ syntax ของ erlang กันก่อน
แต่แทนที่จะเริ่มจากนิยามต่างๆ ลองมาดูตัวอย่างการใช้งานจริงเลยดีกว่า

เริ่มด้วยโจทย์ที่ว่า ถ้ามี http get เข้ามา แล้ว
เราจะ parse เอาค่า parameter ออกมาจาก query String ได้อย่างไร
(น่าเศร้าที่ yaws ไม่มีคำสั่งประเภท request.getParameter
หรือน่าเศร้าตรงที่มันอาจจะมี แต่ผมไม่รู้)

สมมติ url ที่เข้ามาคือ

http://localhost:8080/x.yaws?name=pok&lang=en


ใน yaws เราจะใช้คำสั่ง yaws_api:parse_query(Arg)
ในการ parse query string.
ให้สังเกตดูคำสั่ง จะเห็นว่ามันประกอบด้วย 2 ส่วน คั่นด้วยเครื่องหมาย :
ส่วนแรกคือชื่อ module ซึ่งก็คือ yaws_api
ส่วนหลังคือชื่อ function ใน module นั้นๆ

ผลลัพท์ที่ได้จากคำสั่ง parse_query ก็คือ
list ของ tuple หน้าตาแบบนี้
[{name,"pok"},{lang,"en"}]

list คือ Datatype ชนิดหนึ่ง ถ้าเปรียบเทียบกับสิ่งที่เราคุ้นเคย นั่นก็คือ Array
แต่ implement ของมันจริงๆแล้วเป็นพวก link list

tuple คือ Datatype อีกแบบหนึ่ง มีความหมายคล้ายๆพวก Record
แต่ primitive กว่า, ตัว Record เราสามารถ access by name ได้
แต่ tuple access ได้โดยใช้การระบุลำดับที่

ยกตัวอย่าง
{x,y,z} ถ้าเราต้องการ z เราต้องใช้คำสั่งแบบนี้
Result = element(3, {x,y,z}).

ประโยคนี้บอกอะไรเกี่ยวกับ syntax ของ erlang บ้าง
  1. ชื่อตัวแปร ให้ขึ้นต้นด้วยตัวใหญ่ (พวกเขียน Java จะรู้สึกไม่ชอบใจ)
    ข่าวร้ายตัวแปรสามารถ assign ได้ครั้งเดียว
    assign แล้วห้ามเปลี่ยนแปลงแก้ไข
    ข่าวดี โปรแกรมปราศจาก side-effects
    มี bug น้อย (ถ้าเขียนสำเร็จ)
  2. พวกที่ขึ้นต้นด้วยตัวเล็ก (ในกรณีนี้คือ x หรือ y หรือ z)
    เราเรียกว่า atom, มันเป็นกึ่งๆพวก constant
    ถ้าเปรียบเทียบกับ ruby, มันก็คือ symbol
    ส่วนใน java ไม่มี concept นี้
  3. เราปิด statement ด้วยเครื่องหมาย .


เพื่อให้พิศดารมากขึ้น ประโยคข้างบน สามารถเขียนในลักษณะ pattern matching ได้ดังนี้
{_,_,Result} = {x,y,z}.

เครื่องหมาย _ หมายถึง match เข้ากับ อะไรก็ได้

ย้อนกลับไปที่ผลลัพท์ที่ได้จาก yaws_api:parse_query ใหม่
[{name,"pok"},{lang,"en"}]

จะเห็นว่าชื่อ parameter ได้ออกเป็น Atom
ส่วน value ได้ออกมาเป็น String

ที่นี้ถ้าเกิดเราต้องการ value ของ parameter ที่ชื่อ Name หล่ะ
จะทำได้อย่างไร
เอาวิธี primitive ก่อน
getParameter(_, []) ->
"not found";

getParameter(Name ,L) ->
[H|T] = L,
Key = element(1, H),
if
Key == Name ->
element(2, H);
true ->
getParameter(Name, T)
end.

ดูซับซ้อน แต่อธิบายได้ดังนี้
function หนึ่งๆสามารถเขียนแยกกลุ่มได้ โดยใช้เครื่องหมาย ;
จากข้างบนจะเห็นว่า มันแยกเป็น 2 ส่วนคือ
getParameter(_, [])

กับ

getParameter(Name ,L)

ณ ขณะ runtime erlang จะใช้ pattern matching เพื่อ match parameter
ไล่จากบนลงมาล่าง ถ้าเจอ function ที่ match ได้ ก็จะเข้าไปทำงาน

กรณีของเรา getParameter(_, [])
หมายถึง ตัวแปรแรก match เข้ากับอะไร ข้าไม่สน
แต่ถ้าตัวแปรที่สองเป็น empty list ก็ให้ return "not found" กลับไป

บรรทัดที่เริ่มพิสดาร ก็คือบรรทัดที่ว่า
[H|T] = L

ลองดูตัวอย่าง syntax ของเครื่องหมาย | ก่อน

[1|[]] ได้ผลลัพท์เป็น [1]
[1|[2|[]]] ได้ [1,2]
[1|[2|[3|[]]]] ได้ [1,2,3]

[H|T] = [1]
H ก็คือ 1 ส่วน T คือ []
[H|T] = [1,2,3]
H ก็คือ 1 ส่วน T ก็คือ [2,3]

เจ๋งไหม
ดังนั้น [H|T] = L ก็คือการ extract element แรกสุดของ List นั่นเอง

จากนั้น เราก็ใช้คำสั่ง Key = element(1, H)
assumption ของเราก็คือ H มันมี datatype เป็น tuple แน่นอน
ให้ดึงตัวแรกใน tuple ออกมาเสีย

สุดท้ายก็ใช้ if เปรียบเทียบ
    if 
Key == Name ->
element(2, H);
true ->
getParameter(Name, T)
end.

กรณีที่ Key เท่ากับ Name ของ parameter ที่ต้องการ
ก็ให้ return element ที่สองออกมา
ส่วน true -> เทียบได้กับ else นั่นเอง
ถ้าไม่เจอ ก็ให้หาใน list ที่เหลือ (ที่ได้จากการ map [H|T])

อาจจะดูซับซ้อนนิดหนึ่ง แต่ถ้าเข้าใจแล้ว
มันจะเรียบง่ายอย่างยิ่ง

นี่คือวิธีแบบ primitive
แต่จริงๆแล้วรูปแบบที่ parse_query return กลับมา
มันเป็น pattern ที่ใช้บ่อยๆ (ใน clisp เราเรียก pattern แบบนี้ว่า Alists)
ใน erlang ก็เลยมีคำสั่งที่ช่วยดึงค่าออกมาได้ง่ายขึ้น
getPara(Name, L) ->
V = lists:keysearch(Name, 1, L),
case V of
{value,{_,Result}} ->
Result;
false ->
"not found"
end.


เหนื่อย ยิ่งอธิบายก็ยิ่งลงลึก วันนี้ติดไว้แค่นี้ก่อนครับ

Related link from Roti

งานเขียนโปรแกรมมาราธอน

วันที่ 1-2 กรกฎาคมนี้ ผมมีนัดหมายเข้าร่วมกิจกรรม "งานเขียนโปรแกรมมาราธอน"
โดย Project ที่จะทำก็คือ Server ที่ใช้สำหรับงาน Vehicle Tracking System
ตัว scope งานก็มีนิดเดียว (เพราะมีเวลาแค่นิดหนึ่ง)

1. เขียนโปรแกรมส่วน server ที่ทำหน้าที่รวบรวมข้อมูลที่ submit มาจาก vehicle ผ่านทาง http protocol
2. เขียน web service (rest style) ที่เปิดให้สามารถ query สอบถามสถานะของ vehicle ได้ 
 2.1 สอบถามสถานะของ Vehicle by ID
 2.2 สอบถาม Active Vehicle ที่อยู่ในพื้นที่ที่กำหนด
3. จัดทำ service และ javascript client library ที่เปิดให้ client สามารถ monitor ความเคลื่อนไหวของ vehicle ที่ต้องการได้

ภาษาที่ใช้ก็คือ Erlang
โดย Web Server จะใช้เจ้า Yaws
ส่วน Database จะใช้ Mnesia

ประเด็นที่ผมสนใจในเจ้า erlang ก็คือ
1. มันเป็น Functional Language (code น้อย ต่อยหนัก)
2. Concurrent Model ของมันเป็น Processes โดยมันจัดการ processes เอง
ไม่ได้ใช้ native process หรือ native thread
3. Process คุยกันผ่านทาง Message ซึ่งเป็นแบบ Asynchronous เท่านั้น
4. ย้าย process ข้ามเครื่องได้
5. hot upgrade code ได้
...
เยอะเลยที่ชอบ

ปัญหาตอนนี้มีอยู่อย่างเดียว นั่นคือผมและลูกทืมทั้งหมดยังไม่เคยมีใครเขียน erlang เลย
ความสนุกคงอยู่ตรงนี้แหล่ะ
ตอนนี้สมาชิกในทิืมมีอยู่ 6 คน
มีน้องๆที่บริษัทฯ 2 คน และเป็นเพื่อนหรือน้องใหม่อีก 3 คน

ดังนั้นอาทิตย์นี้และอาทิตย์หน้า blog นี้จะอุทิศให้แก่ erlang

Related link from Roti

Wednesday, June 21, 2006

Natural language Programming

Inform7 เป็น IDE ที่ใช้พัฒนาโปรแกรมประเภท interactive fiction
สิ่งที่น่าสนใจที่สุด ก็คือ syntax ของมัน
ลองดูตัวอย่างเปรียบเทียบวิธีเขียนระหว่าง Inform version 6 กับ version 7
wikipedia-inform
ตัว version 6, syntax มันคุ้นตาโปรแกรมเมอร์อย่างมาก
ส่วน version 7, syntax มันทำให้พวกเขียนภาษาอังกฤษไม่เก่ง อดโปรแกรมมิ่งไปเลย

อย่าลืมตามไปดู Gallery ของ Inform7 ด้วยหล่ะ

Note: คนที่ไม่เคยเล่น interactive fiction มาก่อน (เช่นผม)
จะพบว่าอ่าน Gallery ไม่รู้เรื่องเลย
ดังนั้นควรจะ load game Zork มาทดลองเล่นก่อนอ่าน

Related link from Roti

Sunday, June 18, 2006

Build Dojo

วันนี้ทดลอง build Dojo เอง เพื่อที่จะได้ทำ customize Dojo.js
(Dojo สามารถกำหนดให้เรา รวม object ที่ใช้บ่อยๆ
ให้รวมอยู่ใน file เดียวได้
ส่วนพวกที่ไม่รวมอยู่ใน file นั้น แล้วเกิดต้องการขึ้นมา
Dojo จะใช้ ajax load มาให้อัตโนมัติ)

Dojo ใช้ Ant เป็น build tool
ชอบตรงที่ run ครั้งแรก แล้วมันขึ้นแบบนี้

$ ant -Dprofile=wcf -Ddocless=true release intern-strings
Buildfile: build.xml

-check-config:

-fix-config:
[copy] Copying 5 files to /Users/pphetra/.ant/lib

[echo] +--------------------------------------------------------+
[echo] | Due to some horrendous design decisions by the authors |
[echo] | of Ant, it has been necessaray to install some jar |
[echo] | files to your ~/.ant/ directory. Given the nature of |
[echo] | the problem, it will be necessaray for you to re-run |
[echo] | your build command. |
[echo] | |
[echo] | The Dojo team apologies for this inconvenience. |
[echo] | |
[echo] | The system will now exit. |
[echo] +--------------------------------------------------------+


BUILD FAILED
/Users/pphetra/dev/javascript/release-0.3.1/buildscripts/build.xml:225: Sorry, please re-run your build command, it should work now

ภาษาอังกฤษวันละคำ
horrendous -> extremely unpleasant

ที่น่าสนใจอีกอย่าง ก็คือใน build.xml ของ Dojo
มีการใช้ jython ด้วย

<script language="jython"><![CDATA[
import sys
# make the python standard library avialable
sys.path.append("lib/pyLib.zip")
sys.path.append(".")

# import re
import os
from buildUtil import *

print "Setting version for Dojo to: " + dojo.getProperty("version")
replaceVersion(dojo.getProperty("release_dir") + "/source.__package__.js", dojo.getProperty("version"))
]]></script>


ประเด็นที่น่าสนใจอีกอันของ Dojo
ก็คือการ compress javascript file
ดัว file "dojo.js" ซึ่งเป็น file หลัก จะถูก compress ไว้
หลักการที่ใช้ก็คือ ตัด white space ออกให้หมด
แล้วก็แทนที่ local variable ด้วย running number ให้หมด

โดย Dojo ใช้วิธีการ customize Rhino (เป็นวิธีที่ดี เพราะได้ parser มาฟรีๆ)
ถ้าเราดูใน path $YOUR_DOJO_DIR/buildscripts/lib
จะเห็นว่ามี custom_rhino.diff มาให้ดูด้วย

ทีนี้เราก็สามารถนำเอา custom_rhino.jar ที่ Dojo เตรียมไว้ เอาไปใช้ในงานอื่นๆของเราได้
ถ้าใช้แบบ command line วิธีการใช้ ก็แค่

java -jar custom_rhino -c INPUT > OUTPUT

แต่ให้ดีต้องใช้ใน Ant

<java jar="./lib/custom_rhino.jar" fork="true" output="${dstFile}">
<arg value="-c" />
<arg value="${srcFile}" />
</java>


ประเด็นที่น่าสนใจประเด็นสุดท้าย
ใน dojo มี flash component ด้วย
ซึ่งใช้สำหรับ feature client storage (อ่านรายละเอียดใน blog ของ Brad Neuberg)
ในการ build Dojo จะใช้ mtasc ในการ compile action script

Related link from Roti

Dojo.event.topic

ในการเขียน Reuse Component นั้น
หลักการที่สำคัญก็คือ Loosely Couple,
ให้มี dependency กับ Component อื่นๆให้น้อยที่สุด
ซึ่งใน java สามารถ implement ได้โดยใช้พวก
interface, IOC container, event bus, Listener Pattern, ...

แล้วใน javascript หล่ะ
ถ้าเรามี component ที่ต้องปฏิสัมพันธ์กัน
แต่เราก็อยากให้สัมพันธ์กันแบบ Anonymous, ต่างฝ่ายต่างไม่รู้จักกัน
เราจะ implement ด้วยวิธีไหน

package "dojo.event.topic" ของ Dojo
ช่วยให้เรา implement "Topic-Subscriber" pattern ได้

สมมติเรามี component
var componentA = {
update: function() {
// do some calculate
}
};

var componentB = {
refresh: function() {
// ...
}
};


ถ้าเราต้องการเชื่อมทั้งสองเข้าด้วยกัน
โดยเราอยากให้ componentB.refresh ทำงาน หลังจากที่ componentA.update ทำงาน
var componentA = {
update: function() {
// do some calculate
}
};
dojo.event.topic.registerPublisher("/foo", componentA, "update");

ส่วนฝั่ง B ก็เพิ่ม
var componentB = {
refresh: function() {
// ...
}
};
dojo.event.topic.subscribe("/foo", componentB, "refresh");


วิธี implement ภายในของ dojo ก็น่าสนใจทีเดียว
โดย feature topic จะ base อยู่บน do.event.connect
ซึ่ง base อยู่บนวิธีที่อิง concept จาก Aspect Oriented Programming
ลองดูตัวอย่าง code ของ Dojo ในส่วน JointPoint

dojo.event.MethodJoinPoint = function(obj, methname){
this.object = obj||dj_global;
this.methodname = methname;
this.methodfunc = this.object[methname];
this.before = [];
this.after = [];
this.around = [];
}

ใครที่เคยใช้ AOP ก็คงจะเริ่มเดาได้เลาๆ ว่า feature MethodJoinPoint ทำอะไรได้บ้าง

อ่านรายละเอียดเกี่ยวกับ dojo.event ได้ที่นี่

Related link from Roti