Thursday, January 28, 2010

code <- พิกัด -> diagram

วันก่อนนั่งแกะ code ของ Webmachine เห็น code ส่วนนี้น่าสนใจดี

อันนี้เป็น diagram แสดง flow decision ของ webmachine


ถ้าดูที่ขอบของ diagram เขาจะแสดงเลขพิกัดด้วย


ดู code ที่เขา implement จริง จะเห็นว่าเขาตั้งชื่อ function อ้างอิงเลขพิกัดและ version ของเอกสารด้วย
%% "Forbidden?"
decision(v3b7) ->
decision_test(resource_call(forbidden), true, 403, v3b6);
%% "Okay Content-* Headers?"
decision(v3b6) ->
decision_test(resource_call(valid_content_headers), true, v3b5, 501);
%% "Known Content-Type?"
decision(v3b5) ->
decision_test(resource_call(known_content_type), true, v3b4, 415);
%% "Req Entity Too Large?"
decision(v3b4) ->
decision_test(resource_call(valid_entity_length), true, v3b3, 413);
%% "OPTIONS?"
decision(v3b3) ->
case method() of
'OPTIONS' ->
Hdrs = resource_call(options),
respond(200, Hdrs);
_ ->
d(v3c3)
end;

Related link from Roti

Helloworld with Mochiweb

ใน erlang ถ้าคิดว่าจะใช้อะไรเป็น web server ดี ก็ต้องคิดถึง Yaws ก่อนเพื่อน
แต่ในระยะหลังเริ่มได้ยิน mochiweb และเห็นมี project ใหม่ๆที่นิยมใช้มากขึ้นเรื่อยๆ ก็เลยได้ฤกษ์ทำการทดสอบแบบง่ายๆ โดยจะ implement simple helloworld

เริ่มด้วยการ checkout source code ของ mochiweb ก่อน

svn checkout http://mochiweb.googlecode.com/svn/trunk/ mochiweb
cd mochiweb

จากนั้นก็สั่ง make เพื่อ compile code (แน่นอนว่าต้องติดตั้ง erlang รอไว้ก่อนแล้ว)

เริ่มต้นสร้างโปรเจค โดยสั่ง

escript /PATH_TO/mochiweb/scripts/new_mochiweb.erl helloworld

จะได้ directory หน้าตาประมาณนี้

./helloworld/
./helloworld/support/
run_tests.escript
include.mk
start.sh
start-dev.sh
./helloworld/src/
skel_web.erl
skel_sup.erl
skel_deps.erl
skel_app.erl
skel.hrl
skel.erl
skel.app
Makefile
./helloworld/priv/
./helloworld/priv/www/
index.html
Makefile
./helloworld/include/
./helloworld/doc/
./helloworld/deps/

file ที่เป็นหัวใจหลักก็คือ helloword_web.erl ทำหน้าที่เป็น process ที่ handle incoming request
ให้แก้ไข function loop ดังนี้
loop(Req, DocRoot) ->
"/" ++ Path = Req:get(path),
case Req:get(method) of
Method when Method =:= 'GET'; Method =:= 'HEAD' ->
case Path of
"helloworld" ->
Req:ok({"text/html", "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
<html>
<head><title >Welcome to mochiweb</title></head>
<body>
<h1>Helloworld</h1>
</body>
</html>"
});
_ ->
Req:serve_file(Path, DocRoot)
end;
'POST' ->
case Path of
_ ->
Req:not_found()
end;
_ ->
Req:respond({501, [], []})
end.


สั่ง run server โดยใช้คำสั่ง start-dev.sh
แล้วเรียก http://localhost:8000/helloworld

ถ้าเห็น crash report แบบนี้ อย่าตกใจ

=CRASH REPORT==== 28-Jan-2010::09:31:23 ===
crasher:
initial call: mochiweb_socket_server:acceptor_loop/1
pid: <0.57.0>
registered_name: []
exception error: bad argument
in function erlang:universaltime_to_localtime/1
called as erlang:universaltime_to_localtime({{1969,12,31},
{23,59,59}})

มันเป็น bug ใช้วิธี export TZ=GMT-7 แก้ปัญหาไปพลางๆก่อน

ทดลองแก้ไข code แล้วสั่ง refresh browser จะเห็นว่ามันไม่ reload ให้
ไม่ต้องเศร้าใจ ให้เปิด command prompt สั่ง make , ใน development mode, mochiweb จะตรวจดูว่า binary code เปลี่ยนหรือไม่ ถ้าเปลี่ยนมันจะ reload ให้เอง

ลองเปลี่ยน code ให้ รับ url /helloworld/ANYNAME แล้วแสดง message Hello ANYNAME ดูบ้าง
                case Path of
"helloworld/" ++ ID ->
Msg = io_lib:format("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
<html>
<head><title >Welcome to mochiweb</title></head>
<body>
Hello ~s
</body>
</html>"
, [ID]),
Req:ok({"text/html", Msg});

Related link from Roti

Tuesday, January 26, 2010

Git กับ Remote Branch

สรุปไว้กันลืม

สมมติว่าเรา clone git มาจาก Remote
โดย default เราจะได้ local branch ที่ชื่อ "master" ที่ track กับ remote branch ที่ชื่อ "master"

$ git branch
* master

แต่ถ้าเราสั่งให้ show branch ทั้งหมด

$ git branch -a
* master
origin/HEAD
origin/activity-calendar
origin/master
origin/pilot
origin/pilot_20100128
origin/user-admin

จะเห็นว่า git ทำการ fetch branch ทั้งหมดมาให้เรา เพียงแต่มัน map ขึ้นมาเป็น local branch ให้เราเฉพาะแค่ master

ถ้าเราสั่ง git fetch สิ่งที่เกิดก็คือ git จะทำการดึงข้อมูลการเปลี่ยนแปลงทุกอย่างของ server มาที่เครื่องเรา แต่จะยังไม่ update Working copy ให้เรา

ส่วน git pull ก็เทียบได้กับการสั่ง git fetch แล้วตามด้วย git merge โดยจะ merge จะเฉพาะ current HEAD ของเรา (ถ้า HEAD คือ master ก็จะ merge เฉพาะ master)

ที่นี้ถ้าเราต้องการ track remote branch บนเครื่องเรา สิ่งที่ต้องทำก็คือ

$ git checkout -b pilot origin/pilot
Branch pilot set up to track remote branch refs/remotes/origin/pilot.
Switched to a new branch "pilot"


ปัญหาที่เกิดบ่อยๆก็คือ เราสั่ง track remote branch โดยตั้งชื่อ local branch ไม่ตรงกับ remote branch เช่น

$ git checkout -b myb1 origin/b1
Branch myb1 set up to track remote branch refs/remotes/origin/b1.
Switched to a new branch "myb1"

เวลาเราแก้ไข file ใน branch myb1 เสร็จแล้วอยาก push ไปที่ server

$ vi MYFILE

$ git add MYFILE
$ git commit -m 'Add MYFILE'

$ git push
Everything up-to-date

จะเห็นว่ามันไม่ยอม push commit ใหม่ของเราไปที่ remote repository
ที่เป็นเช่นนี้เพราะถ้าเราสั่ง git push โดยไม่มี parameter สิ่งที่ git ทำก็คือ มันจะ push เฉพาะชื่อ branch ที่ตรงกันของทั้งสองฝั่ง

การแก้ไขก็คือ เปลี่ยนตัวแปร configuration push.defaut ให้เป็น tracking เพื่อที่จะให้มัน push เฉพาะ branch ที่มี tracking กับ remote branch โดยไม่สนใจเรื่องชื่อตรงกัน

$ git push
Everything up-to-date
pphetra@[~/t2]
$ git config push.default tracking
pphetra@[~/t2]
$ git push
Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 248 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To git@github.com:pphetra/test.git
7d96d34..0dd66fa myb1 -> b1


ที่นี้ถ้าเราต้องการสร้าง branch ใหม่บน remote หล่ะ
เริ่มจากการสร้าง branch บน server

$ git push origin origin:refs/heads/b4
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:pphetra/test.git
* [new branch] origin/HEAD -> b4
$ gitx

$ git branch -a
* b1
b2
master
myb1
remotes/origin/HEAD -> origin/master
remotes/origin/b1
remotes/origin/b2
remotes/origin/b4
remotes/origin/master

จากนั้นก็สั่ง checkout ตามปกติได้เลย

$ git checkout -b b4 origin/b4
Branch b4 set up to track remote branch b4 from origin.
Switched to a new branch 'b4'

Related link from Roti

Thursday, January 21, 2010

เล่น Lego

Lego เป็นของเล่นที่ดี แต่ราคาแพง สมัยเด็กๆจำได้ว่าอยากได้มาก ต้องรอจนมัธยมแล้ว แม่ถึงซื้อให้เล่น 1 ชุด โดยมีข้อแม้ว่าซื้อให้ ผมกับน้องสองคน ไม่ใช่ของใครคนใดคนหนึ่ง

พอโตขึ้นมามีลูกของตัวเอง ช่วงน้องปัณณ์อายุ 4 ขวบ อาเก่งกับอาหนึ่งซื้อ Lego ให้เป็นของขวัญ, ปรากฎว่าน้องปัณณ์ชอบของเล่นชิ้นนั้นมาก นั่นต่อ Lego ตามจินตนาการไปเรื่อย, หน้าที่พ่อก็เลยต้องคอยหา Input เข้าไปเรื่อยๆ เพื่อให้เกิดความท้าทายและไม่น่าเบื่อ (ตามกฎของ Flow)



ในกรณีของ Lego นี้, input มีอยู่สองสามแบบก็คือ
1. ซื้อชุดใหม่ให้เขา ให้เขาได้ตื่นเต้นกับแบบใหม่ๆ อันนี้ทำบ่อยๆไม่ดีแน่ เพราะราคามันสูงมากและถ้าได้มาง่ายไป มันจะไม่มีค่าเท่าไร
2. หา MOC (My Own Creation) ที่คนอื่นสร้างไว้มาให้เขาดู เขาจะได้เกิดแรงบันดาลใจ และเรียนรู้ว่ามันมีความเป็นไปได้ในการต่อเยอะแยะไปหมด
3. หาเนื้อหาใหม่ๆ ที่ทำให้เขาใช้ Lego เป็นเครื่องมือในการหมกมุ่นกับเนื้อหานั้นๆ เช่นช่วงวันเด็กผมพาไปปัณณ์ไปดูงาน Lego ที่เขาเอา Lego มาต่อเป็นเมืองจำลอง กลับบ้านมาก็เลยมีการทำฉากจำลองต่างๆกันบ้าง หรือแนะนำเกมส์ Spore ให้เขาเล่น ซึ่งพอเขาเล่น Lego เขาก็จะพยายามต่อเป็นสัตว์ประหลาดในเกมส์

อันนี้เป็นผลงาน MOC ของลูกชาย ที่ต่อเมื่อคืนนี้





ส่วนของพ่อได้ลำนี้ออกมา



หลังจากที่ลูกหมกมุ่นกับ Lego ได้เกือบ 1 ปี ตอนนี้ต่อมเก็บกดของพ่อเด็กเริ่มแตกแล้ว อันนี้เป็นสิ่งที่น่ากลัวมาก เพราะเราเป็นคนถือกระเป๋าเงิน, ลูกติด แต่มันไม่มีเงิน มันก็เลยซื้อไม่ได้ แต่ถ้าพ่อติด และพ่อมีเงิน อันนี้สิน่าหวาดเสียว

Related link from Roti

Tuesday, January 19, 2010

MultiMethod with Visitor Pattern

ต่อจากเมื่อวาน Link
อาจารย์โป้งได้อธิบายให้ฟังด้วยว่า วิธีหนึ่งที่เขานิยมใช้ implement multiple dispatch ใน Language ที่ support แต่ single dispatch ก็คือ Visitor Pattern (อาจารย์โป้งไม่ได้ใช้วิธีนี้)

ผมก็เลยเอาตัวอย่างเมื่อวานมาลองเขียนดู
เริ่มแรกก็คือ ให้เจ้า concrete animal ของเรามี accept method ที่รับ Visitor interface ก่อน
public interface Animal {
public void accept(IVisitor visitor);
}

public class Cat implements Animal {

@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}

}

public class Dog implements Animal {

@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}

}

จากนั้นก็ สร้าง Visitor Interface ที่มี method visit ครบทุก concrete animal ของเรา
public interface IVisitor {
public void visit(Cat cat);
public void visit(Dog dog);
}

ทดลอง feed อาหารโดยใช้ pattern ใหม่นี้
Cat cat = new Cat();
Dog dog = new Dog();

IVisitor bone = new IVisitor() {

@Override
public void visit(Cat cat) {
System.out.println("I don't like this. Meaw Meaw!!!");

}

@Override
public void visit(Dog dog) {
System.out.println("I 'm full. Hong Hong!!!");

}

};

IVisitor fish = new IVisitor() {

@Override
public void visit(Cat cat) {
System.out.println("I like this. Meaw Meaw!!!");

}

@Override
public void visit(Dog dog) {
System.out.println("Yes, I can eat this. Hong Hong!!!");

}

};

cat.accept(bone);
dog.accept(bone);

cat.accept(fish);
dog.accept(fish);

Related link from Roti

Monday, January 18, 2010

MultiMethod

เมื่อวันเสาร์ไป compkucamp มา เจออาจารย์โป้งเข้าก็เลยถามว่า ช่วงนี้ทำอะไรอยู่, อาจารย์โป้งก็ตอบว่ากำลัง implement multimethod บน c++ อยู่ ว่าแล้วก็ควักโน๊ตบุ๊คออกมาแสดง อาจารย์โป้งใช้ software ได้ผสมปนเปมาก เริ่มจากเปิด microsoft visual c++ ขึ้นมา จากนั้นก็ switch ไปใช้ terminal บน mac เพื่อใช้ django generate project files จากนั้นก็ show file content ให้ดูโดยใช้ textmate

หลังจากกลับมาบ้าน และส่งลูกเข้านอนหมดแล้ว เพื่อแก้ข้อสงสัยที่ฟังมา ก็เลยต้องเข้า wikipedia ไปหาอ่านเรื่อง Multimethod หรือ Multiple Dispatch บ้าง

ในการทำความเข้าใจกับเรื่องนี้ เราก็ควรจะเริ่มจาก basic สุดก่อนก็คือ Single Dispatch ซึ่งใช้ใน Java, C++, Smalltalk, Objective-C

ลองดู code นี้
public abstract class Animal {

public abstract void feed(Food food);

}

public abstract class Food {

}

public class Fish extends Food {

}

public class Bone extends Food {

}

public class Dog extends Animal {

@Override
public void feed(Food food) {
System.out.println("I 'm full. Hong Hong!!!");
}

}

public class Cat extends Animal {

@Override
public void feed(Food food) {
System.out.println("I 'm full. Meaw Meaw!!!");
}

public void feed(Bone food) {
System.out.println("I don't like this. Meaw Meaw!!!");
}

}



ถ้าลอง run แบบนี้ดู
public class Runme {

public static void main(String[] args) {
Animal a = new Dog();
Animal b = new Cat();

Bone bone = new Bone();
a.feed(bone);
b.feed(bone);

}

}


กรณีที่เป็น single dispatch ผลลัพท์ที่ได้ก็คือ
I 'm full. Hong Hong!!!
I 'm full. Meaw Meaw!!!

จะเห็นว่า single dispatch จะตัดสินใจเลือก method โดยดูแค่ว่าจะเลือกให้ class ไหนรับผิดชอบในการ handle การ call, โดยไม่ได้สนใจ type ของ arguments
ส่วน multiple dispatch มันจะเลือก method โดยดู type ของ arguments ด้วย
ถ้าทดลองนำ code ข้างบน ไป run ใน groovy ซึ่งเป็น multiple dispatch ผลลัพท์ทีได้ก็คือ
I 'm full. Hong Hong!!!
I don't like this. Meaw Meaw!!!

Related link from Roti

Thursday, January 14, 2010

เรียนสีน้ำ

เมื่อวานที่ Opendream มีการจัดกิจกรรมเรียนวาดภาพสีน้ำครั้งแรก โดยมีน้องแพ็ครับหน้าที่เป็นผู้สอน, course แรกเริ่มด้วยการแจกรูปลายเส้นขนมเค็ก พร้อมกับให้ดูภาพจริงบน computer จากนั้นก็สั่งสั้นๆว่า "ระบาย"

ผู้เรียนทั้งหลายก็ก้มหน้าก้มตาระบาย (บางคนก็ประทัวงบ้างว่า ยังไม่สอนเลย จะระบายได้อย่างไร)



ผมชอบวิธีเรียนแบบนี้นะ ทักษะการระบายสีคือการปฎิบัติ อยากรู้ก็ต้องระบาย จะมามัวเรียนว่าจับพู่กันอย่างไร, หรือเรียนรู้ basic introduction ของสีน้ำ มันก็ไช่ที่

ผมเคยหัดเขียนสีน้ำ เมื่อตอนยังหนุ่มๆเหมือนกัน สมัยนั้นพบว่า อุปสรรคสำคัญของคนที่อยากจะเรียนสีน้ำ ก็คือ "ความกลัว" กลัวว่าจะวาดหรือระบายออกมาไม่สวย. ก็เลยไม่กล้าลงมือทำ น่าสนใจตรงที่ว่า ความกลัว นั้นมันเกิดมาได้อย่างไร. เป็นผลจากการศึกษาหรือ culture หรือเป็น Generalize ของมนุษย์

เพื่อเป็นกำลังใจให้น้องๆที่ชอบคาดหวังกับตัวเอง ก็เลยไปค้นรูปสีน้ำรูปแรก(18 ปีแล้ว) มาให้ดู


ถ้าคาดหวังไว้สูงเกินจริง หรือมีความคิดลบกับตัวเอง ก็คงเลิกไปแล้ว
แต่ถ้าวาดไปเรื่อยๆ ไม่กลัว สักวัน(ของผมนี่หลายปีอยู่) มันก็จะพอดูได้ขึ้นมา (อันนี้ก็ราวๆ 13 ปีแล้วมั้ง)



ประเด็นที่สำคัญของการระบายสีน้ำสำหรับผม ก็คือ ความสุขอยู่ที่ระหว่างการระบาย ถ้าไป focus ผิดที่ก็จะไปวางความสุขไว้ที่ผลลัพท์ อยากเห็นผลลัพท์ที่สวยงาม จริงๆแล้ว ผลลัพท์ที่สวยงามเป็นแค่ผลพลอยได้

Quote ที่ตรงที่สุดสำหรับเรื่องนี้ คือ

Experience is what you get when you didn't get what you wanted.

เวลาลงสี จะเห็นได้ชัดเลยว่า มันออกมาไม่เหมือนที่เราอยากได้

Related link from Roti

Thursday, January 07, 2010

เล่นน้ำฝน

เมื่อวานฝนตกหนัก โชคดีที่หนีออกจาก Opendream ตั้งแต่บ่ายแก่ๆ ก็เลยไม่ต้องผจญกรรม(รถติด)มากนัก

ไปถึงบ้าน ฝนกำลังกระหน่ำ ก็เลยชวนลูกชายเบอร์สองออกไปเล่นน้ำฝนกัน (คนแรกไม่ชวน เพราะว่าพ่อพาขี่จักรยานตากฝนไปโรงเรียนบ่อยแล้ว)



เล่นได้พักเดียว ฟ้าผ่าเปรี้ยง เจ้าลูกชายวิ่งแนบเข้าบ้าน

Related link from Roti

Tuesday, January 05, 2010

Dynamically generate code in Erlang

ปัจจุบัน application framework ทั้งหลาย พยายามจะทำให้ชีวิตเราง่ายขึ้น ด้วยการลด noise ที่เราไม่จำเป็นต้องเห็น, generate code ที่จำเป็นต้องใช้ให้เรา

Chicago Boss Framework ก็อยู่ในกระแสนี้เช่นกัน ตัว Relation Mapping layer ของมันก็พยายามจะลดรูปให้เหลือน้อยที่สุด, คำถามสำหรับผมก็คือ ใน Erlang นี่เขาใช้เทคนิคอะไรมาช่วย generate code หรือทำ magic บ้าง

ลองดูตัวอย่างการใช้งานก่อน เริ่มด้วยการ define Domain model
-module(blog_post, [Id, Title, Text, AuthorId]).
-compile(export_all).
-belongs_to(author).

-module(author, [Id, Name]).
-compile(export_all).
-has_many(blog_posts).

เทคนิคแรกที่เขาใช้ก็คือ Parameterized Module ซึ่งช่วยให้ Module มีพฤติกรรมในลักษณะ OOP ได้
ลองดู code ตอนที่เรา new instance domain ของเรา
FakeAuthor = (author:new(id, "YOUR NAME")):save(),
BlogPost = blog_post:new(id,
"BLOG TITLE",
"BLOG CONTENT",
FakeAuthor:id()),
SavedBlogPost = BlogPost:save(),

จะเห็นว่า module ของเรามี function "save", "getter"(ตรงที่ get id จาก fakeauthor) เพิ่มขึ้นมาให้เองโดยที่เราไม่ต้องเขียน คำถามก็คือ เขาใช้เทคนิคอะไรในการ generate code ส่วนนี้

เริ่มแรกสุด code ในส่วน module นี้จะไม่ load ขึ้นมาผ่านกลไกปกติ แต่จะทำผ่านกลไกของตัวเอง โดยเริ่มต้น มันจะทำการ parse erlang file โดยใช้ function epp:parse_file ผลที่ได้เราเรียกว่า Form

ทดลองใช้ epp:parse_file กับ ตัวอย่างโปรแกรมที่น้องป้อเขียน
-module(p).
-export([start/0, say_something/2]).
say_something(What, 0) ->
done;
say_something(What, Times) ->
io:format("~p~n", [What]),
say_something(What, Times - 1).
start() ->
spawn(tut14, say_something, [hello, 3]),
spawn(tut14, say_something, [goodbye, 3]).


จะได้ Form ที่มีหน้าตาประมาณนี้
{ok,[{attribute,1,file,{"p.erl",1}},
{attribute,1,module,p},
{attribute,2,export,[{start,0},{say_something,2}]},
{function,3,say_something,2,
[{clause,3,
[{var,3,'What'},{integer,3,0}],
[],
[{atom,4,done}]},
{clause,5,
[{var,5,'What'},{var,5,'Times'}],
[],
[{call,6,
{remote,6,{atom,6,io},{atom,6,format}},
[{string,6,"~p~n"},{cons,6,{var,...},{...}}]},
{call,7,
{atom,7,say_something},
[{var,7,'What'},{op,7,'-',...}]}]}]},
{function,8,start,0,
[{clause,8,[],[],
[{call,9,
{atom,9,spawn},
[{atom,9,tut14},
{atom,9,say_something},
{cons,9,{...},...}]},
{call,10,
{atom,10,spawn},
[{atom,10,tut14},
{atom,10,say_something},
{cons,10,...}]}]}]},
{eof,11}]}

จะเห็นว่ามีลักษณะเป็น Abstract Syntax Tree + Meta Data

พอได้ form มา เจ้า ChicagoBoss ก็จะทำการแทรก,แปลง code ให้เป็นไปตามต้องการ โดยมันจะใช้ function ใน library erl_syntax ช่วยในการ generate Form

ตัวอย่าง code ในส่วนที่สร้าง getter
parameter_getter_forms(Parameters) ->
lists:map(fun(P) ->
erl_syntax:add_precomments([erl_syntax:comment(
[lists:concat(["% @spec ", parameter_to_colname(P), "() -> ", P]),
lists:concat(["% @doc Returns the value of `", P, "'"])])],
erl_syntax:function(
erl_syntax:atom(parameter_to_colname(P)),
[erl_syntax:clause([], none, [erl_syntax:variable(P)])]))
end, Parameters).

parameter_setter_forms(ModuleName, Parameters) ->
lists:map(
fun(P) ->
erl_syntax:add_precomments([erl_syntax:comment(
[
lists:concat(["% @spec ", parameter_to_colname(P), "( ", P, "::",
case lists:suffix("Time", atom_to_list(P)) of
true -> "tuple()";
false -> "string()"
end, " ) -> ", inflector:camelize(atom_to_list(ModuleName))]),
lists:concat(["% @doc Set the value of `", P, "'."])])],
erl_syntax:function(
erl_syntax:atom(parameter_to_colname(P)),
[erl_syntax:clause([erl_syntax:variable("NewValue")], none,
[
erl_syntax:application(
erl_syntax:atom(ModuleName),
erl_syntax:atom(new),
lists:map(
fun
(Param) when Param =:= P ->
erl_syntax:variable("NewValue");
(Other) ->
erl_syntax:variable(Other)
end, Parameters))
])]))
end, Parameters).


พอ transform code เสร็จ ก็จะทำการ compile เป็น binary ด้วย function compile:forms

compile เสร็จก็จัดการ load เข้า runtime โดยใช้ function code:load_binary

Related link from Roti

Monday, January 04, 2010

P07 กับ List Moand

เห็นน้องป้อทำโจทย์ P07 ของ Ninety-Nine Lisp Problem ก็เลยกลับไปอ่านวิธีทำของตัวเอง แล้วก็พบว่าถึงแม้เวลาจะผ่านมา 4 ปีแล้ว ตัวเองก็ยังไม่เคยเข้าใจคำตอบของ Conor McBride เลย วันนี้ก็เลยได้ฤกษ์ทำความเข้าใจกับ List Monad ที่ Conor ใช้ตอบคำถามผม

เริ่มด้วยคำอธิบาย Monad ที่บอกว่า Monad คือ "an abstract datatype of actions" ซึ่งมีนิยามง่ายๆแค่นี้
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a


มันคือ pattern ฉนั้นอย่าพึ่งไปพยายามจินตนาการว่า มันทำอะไรได้บ้าง
หันกลับมาดู context ของ List บ้าง เรารู้ว่า type ของ List คือ [a] จะเห็นว่า type [a] มันสามารถมองเป็น m a ได้ (เพราะ type constructor มันมี free variable 1 ตัวเหมือนกัน) ฉนั้นเราลองมา implement ให้ List เป็น Monad กัน

เริ่มจาก implementation ที่ง่ายที่สุดก่อน ก็คือ return, return มี type เป็น a -> m a ฉนั้นกรณีของ List, คำสั่ง return 4 ก็ควรได้ค่า [4] ออกมา
instance Monad [] where
return x = [x]

ตัวถัดมาก็คือ function >>= นิยามของ type มันคือ m a -> (a -> m b) -> m b
เปลี่ยน m a ให้เป็น [a] จะได้ [a] -> (a -> [b]) -> [b] จะเห็นว่า definition มันไกล้เคียงกับ map function ที่มี type เป็น [a] -> (a -> b) -> [b] ดังนั้นเราสามารถ implement >>= โดยใช้ map และ concat
หน้าตาออกมาดังนี้
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)

ได้นิยามของ List ในมุมมองของ Monad ออกมาอย่างงงๆ
แล้วมันมีประโยชน์อะไรหล่ะ ที่ทำ List type ให้เป็น instance ของ Monad type

ลองมาหัดใช้ List Monad ทำโจทย์
ถ้าให้ list ของ [1..10] มาให้หาตัวเลข 2 ตัวที่ผลคูณมีค่า = 16

ถ้าใช้ List Monad ก็จะเขียนแบบนี้
guard True xs = xs
guard False xs = []

solve = do
x <- [1..10]
y <- [x..10]
guard (x * y == 16) (return (x,y))

ซึ่งมี form ที่ไกล้เคียงกับ solution ที่ใช้ List comprehension
[(x,y) | x <- [1..10], y <- [x..10], x * y == 16]

กลับมาที่ solution ที่ McBride เขียนตอบผม
flat1 :: Store a -> a
flat1 (E a) = return a
flat1 (S xs) = xs >>= flat1

ถึงตอนนี้พอจะรู้เรื่องกับคำอธิบายของเขาแล้ว
Your (flat xs) on a list of stores becomes my (xs >>= flat1), systematically lifting the operation on a single store to lists of them and concatenating the results. The return operation makes a singleton from an element. This way of working with lists by singleton and concatenation is exactly the monadic structure which goes with the list type, so you get it from the library by choosing to work with list types. In Haskell, when you choose a typed representation for data, you are not only choosing a way of containing the data but also a way to structure the computations you can express on that data

Related link from Roti

Sunday, January 03, 2010

บ้านน้ำแข็ง

ช่วงปีใหม่ หนีร้อนไปอยู่บ้านย่า พอกลับมาถึงบ้านตัวเองพบว่า ตู้เย็นที่ defrost ไว้ น้ำในช่องน้ำแข็งไหลลงมาอยู่ในถาดแล้วกลายเป็นน้ำแข็งแผ่นใหญ่ไปแล้ว เห็นแล้วก็เลยได้ idea ชักชวนลูกชายให้มาทำบ้านน้ำแข็งของชาวเอสกิโมกัน

เริ่มด้วยการสอนให้ลูกชายใช้ฆ้อนกับสิ่วตอกน้ำแข็งเป็นก้อนพอเหมาะ จากนั้นก็เรียงกันเป็นทรงกลม เชื่อมประสานก้อนน้ำแข็งด้วยเกลือ เสร็จแล้วก็ถ่ายรูปเพื่อให้(พ่อและ)ลูกได้ชื่นชมผลงาน

Related link from Roti

Saturday, January 02, 2010

Parameterized Module

วันนี้นั่งดู code ของ erlang web framework ที่ชื่อ "Chicago Boss" แล้วก็สงสัยว่า syntax ของ model/domain ของมัน ซึ่งดูไม่คุ้นตาเลย

เริ่มจากตัว domain model code ก่อน, เจ้า chicago boss กำหนดให้เขียนแบบนี้
-module(blog_post, [Id, Title, Text, AuthorId]).
-compile(export_all).

เวลาเอาไปใช้งาน ก็จะใช้แบบนี้
BlogPost = blog_post:new(id, "my title", "la la", SomeAuthorId)
BlogPost:save()

เริ่มจาก บรรทัดนี้
-module(blog_post, [Id, Title, Text, AuthorId]).

syntax การ declare แบบนี้ มีศัพท์เฉพาะ เรียกว่า Parameterize Module
parameter ที่ตามหลัง module name (ในกรณีนี้ก็คือ Id, Title, Text, …) จะกลายเป็นตัวแปรที่สามารถ access ได้จากทุก function ที่อยู่ใน module นั้น

ส่วนที่น่าสนใจถัดไปก็คือ
BlogPost = blog_post:new(id, "my title", "la la", SomeAuthorId)

function "new" คือ function ที่ erlang generate ให้เมื่อเรา define module ให้เป็น parameterized โดย parameters ที่เราผ่านค่าเข้าไปจะถูกนำไป initialize ค่าตัวแปรที่เราประกาศไว้ และค่าที่ return กลับมาจาก function new ก็คือ instance ของ module นั้นๆ

หลังจาก new module instance แล้ว เราก็สามารถเรียกใช้ function ใน module instance นั้นๆ โดยใช้ syntax แบบนี้
BlogPost:save()

จะเห็นว่าเราเรียก function จาก instance ของ Module ซึ่งภายใน save function ถ้ามีการ access ตัวแปร Title, ตัวแปร Title ก็จะเป็นตัวแปรที่อยู่ใน scope ของ instance BlogPost (มันคือ OOP นั่นเอง)

ถ้าใครอยากรู้ว่า Parameterized Module เขาออกแบบมาเพื่อช่วยแก้ปัญหาอะไร ขอแนะนำให้อ่าน paper นี้ เฉพาะส่วน introduction ก็พอ

Related link from Roti

Friday, January 01, 2010

Conversations with Architects

หลังจากมีลูกครบสามแล้ว นานๆทีถึงจะมีเวลาเข้าร้านหนังสือ เมื่อวานได้โอกาสดีแวบเข้าไปฉกหนังสือน่าอ่านมาหนึ่งเล่ม ชื่อว่า "คุยกับต้นกล้าสถาปนิก - Conversations with Architects" เป็นหนังสือรวมสัมภาษณ์เหล่าสถาปนิกรุ่นใหม่ (เนื่องจากคนสัมภาษณ์อายุราว 60 แล้ว, นิยามของรุ่นใหม่ก็เลยอายุ ~40 up ทุกคนเลย)

สิ่งที่ขอบสำหรับหนังสือเล่มนี้ก็คือ ผู้สัมภาษณ์ก็คือคุณนิธิ สถาปิตานนท์ ซึ่งประสบความสำเร็จในวิชาชีพจนได้รับการยกย่องเป็นศิลปินแห่งชาติ สาขาศิลปะสถาปัตยกรรม เมื่อผู้สัมภาษณ์เก่งและมีประสบการณ์ขนาดนี้ บทและประเด็นสัมภาษณ์ก็เลยชวนอ่านยิ่งนัก

อ่านวิชาชีพคนอื่นแล้วก็ต้องหันมามองตัวเอง เมื่อเทียบกับวิชาชีพอื่นๆแล้ว วิชาชีพ IT ยังเป็นอะไรที่ใหม่มากอยู่ สิ่งที่ผมอยากเห็นก็คือการรวมตัวกันเป็นชุมชน (Community of Practice) เพื่อที่จะแลกเปลี่ยน เรียนรู้ร่วมกัน, โดยการเกาะกลุ่มนั้นเกิดจาก passion ในการเรียนรู้ มากกว่าการเล็งผลประโยชน์ทางธุรกิจ

เมื่อพูดถึง Comminuty of Practice แล้ว ก็เลยขอประชาสัมพันธ์ วันเสาร์ที่ 9 มกราคม 2553 นี้ @roofimon กับผม และเพื่อนๆ จะจับเข่าคุยกันเรื่อง Functional Programming โดยสถานที่ได้รับความเอื้อเฟ้ือจาก Opendream ใครที่สนใจไม่เป็นทางการ เนื้อหาที่ได้จะน่าสนใจยิ่งนั้น คุณสมบัติขั้นต่ำสำหรับผู้เข้าร่วมก็คือ ผู้เข้าร่วมต้องไม่อายที่จะถามคำถามที่ตัวเองสงสัย และไม่กลัวว่าคำถามนั้นจะเป็นคำถามที่แสดงความไม่รู้ของตัวเอง ( :) โลกนี้มีใครบ้างที่รู้อะไรจริงๆ)

Related link from Roti

Tuesday, September 22, 2009

เมื่อวานเศร้ามาก อุตส่าห์นั่งปั่น code เพื่อเตรียม release แต่กลับพบว่า มันเปิดบน IE ไม่ได้ ช่วงหลังๆประมาทเพราะมีแต่ minor changed ทั้งนั้น เลยไม่ได้ทดสอบบน IE
แต่ไม่เป็นไร git มีเครื่องมือที่เรียกว่า 'bisect' สำหรับหาว่า commit ไหนเป็นตัวปัญหา

การใช้งานเริ่มด้วยคำสั่ง

$ git bisect start

จากนั้นก็ mark ว่า revision ที่กำลังทำงานอยู่มันไม่ดี

$ git bisect bad

บอกมันด้วยว่า revision ไหนที่มันยังดีอยู่

$ git bisect good

git ก็จะจัดการ checkout revision ที่อยู่ตรงกลางระหว่าง bad กับ good ให้
เราก็แค่บอกมันว่า revision ปัจจุบันที่ใช้งานอยู่มัน bad หรือ มัน good
กรณีของผม ผมทดสอบแล้วยัง error อยู่ก็เลยสั่ง

$ git bisect bad
Bisecting: 10 revisions left to test after this
[2c6383f3664f50b2121ba5d455d595a60c3aa85c] prototype for flex chart component.

ทำไปเรื่อยๆ จนกว่าจะจับ commit ที่มีปัญหาได้ ซึ่งมันจะฟ้องว่า

$ git bisect good
4bc966d79eb3cd7ec10672863b6c97b26f401ea7 is first bad commit
commit 4bc966d79eb3cd7ec10672863b6c97b26f401ea7
Author: pphetra
Date: Thu Aug 6 05:58:20 2009 +0000

หลังจากได้ revision ที่เป็นปัญหา ก็เป็นหน้าที่เราแล้วที่ต้องไปไล่ดู diff file ว่าอะไรที่ทำให้มันพัง

ก่อนจะออกไปก็ให้ clear สถานะ git bisect ด้วยคำสั่ง

git bisect reset


Note: กรณีที่มี Makefile ให้ run จาก command line, เราสามารถใช้คำสั่ง git bisect run ได้เลย

Related link from Roti

Monday, July 20, 2009

ทำ DSL ด้วย parser combinator

ช่วงนี้ทำระบบ billing ด้วย Grails อยู่ มันมีโจทย์ว่า user ต้องสามารถกำหนด policy การคิดเงินค่าโทรศัทพ์ได้เอง โดยรูปแบบของการคิดเงินถ้าเขียนออกมาเป็น text ก็ประมาณนี้
ถ้าเวลาที่โทรอยู่ในช่วงหนึ่งนาทีแรก ให้คิดเงินเต็มหนึ่งนาที ส่วนเวลาที่เกินให้ปัดที่หน่วยละ 6 วินาที โดยคิดหน่วยละ 10 % ของราคาต่อนาที

version แรกสุดที่ผมทำ หน้าตาออกมาประมาณนี้
[minimum: 60, nextCharge: 6, rateFunc: { x -> x * rate * 0.1}]

ไม่ต้องบอกก็รู้ตัวว่า ไม่มี user ที่ไหน config ได้แน่ มี closure หน้าตาประหลาดโผล่มาแบบนี้ (จริงๆแล้วมันคือ code ของ groovy ที่พร้อมจะ run นั่นเอง)

version ที่สอง ผมก็เลยปรับให้มันกระเดียดไปทางภาษาคนมากขึ้น หน้าตาออกมาแบบนี้
minimum 60 sec , nextcharge 6 sec : 10 %
หรือจะใช้หน่วย minute ด้วยก็ได้
minimum 1 min, nextcharge 6 sec : 10 %

เมื่อความต้องการเป็นแบบนี้ ก็ต้องหาวิธี implement, วิธีที่คุ้นเคยมากสุดก็คือใช้ Antlr เขียน Parser แต่บังเอิญเป็นคนขี้เบื่อ ก็เลยมองหาวิธีอื่นแทน ช่วงนี้ Scala กำลังมาแรง ประกอบกับเคยเห็นว่ามันทำ Parser combinator ได้ด้วย ก็เลยหวยออกที่ Scala + Parser Combinator

เริ่มแรกสุด parser ของเรา extends จาก StandardTokenParsers (มากับ Core ของ Scala อยู่แล้ว ไม่ใช่ external library)

class RateParser extends StandardTokenParsers {

}

กำหนด delimiters และ reserved word

lexical.delimiters ++= List(":", "%", ",")
lexical.reserved += ("minimum", "nextcharge", "min", "minute", "sec", "second", "m", "s")

จากนั้นก็ define grammar
ถ้าดู code จะเห็นว่าเรา define parser ย่อยๆเต็มไปหมด อันนี้แหล่ะคือความหมายของ combinator นั่นคือเราเขียน parser ใหญ่ๆ ด้วยการการเอา parser เล็กๆย่อยๆมาประกอบกันนั่นเอง
  def rule: Parser[RateStrategy] = minimum ~ nextcharge ~ percent ^^
{ case (min ~ next) ~ percent => new RateStrategy(min, next, percent)}

def minimum: Parser[Int] = "minimum" ~> unit <~ ","

def nextcharge: Parser[Int] = "nextcharge" ~> unit <~ ":"

def percent: Parser[Int] = numericLit <~ "%" ^^ (_.toInt)

def unit: Parser[Int] = minuteUnit | secondUnit

def minuteUnit: Parser[Int] = numericLit <~ ("min" | "minute" | "m") ^^ (_.toInt * 60)

def secondUnit: Parser[Int] = numericLit <~ ("sec" | "second" | "s") ^^ (_.toInt)

จะเห็นว่ามี operator หน้าตาแปลกๆ เต็มไปหมด ไม่ต้องตกใจ มาลองดูแบบง่ายสุดก่อน เริ่มที่การ parse หน่วยเวลากันก่อน
จากโจทย์ของเขา จะเห็นว่าเราจะทำการ parse พวก "1 min", "60 sec"
กรณี minute จะเห็นว่า code หน้าตาแบบนี้
  def minuteUnit: Parser[Int] = numericLit <~ ("min" | "minute" | "m") ^^ (_.toInt * 60)

def minuteUnit: Parser[Int]
ก็คือการ define method ที่ return parser ที่ return Integer (Higher order function)
numericLit <~ ("min" | "minute" | "m")
ก็คือ ระบุว่า ประโยคจะเริ่มต้นด้วย integer จากนั้นจะตามด้วย "min" หรือ "minute" หรือ "m"
เครื่องหมาย "<~" เป็น operator ที่ extend มาจาก operator "~"
operator "~" มีความหมายว่า ถ้า parse argument ทางซ้ายสำเร็จ ก็ให้ทำ ทางขวาต่อ (chain)
แต่ถ้าไม่สำเร็จ ก็ abort
ส่วน "<~" เป็นการเพิ่มความหมายว่า argument ท่ีอยู่ทางซ้ายถือเป็นตัวที่เราสนใจ ให้ ignore argument ที่อยู่ด้านขวาไปได้เลย
Note: แน่นอน เมื่อมี "<~" ก็เลยมี "~>" ด้วย
^^ (_.toInt * 60)
เราเรียก transforms operator นั่นคือ ในกรณีนี้แทนที่จะ return String ตัวเลขนาทีไป เราจะเปลี่ยนให้มันเป็น integer ก่อน

ความยุ่งยากอันถัดไปก็คือ ต้องให้มันเรียกใช้จาก Groovy ได้ (โปรเจคหลักเป็น Groovy)
โชคดีที่ Class ที่ Scala compile ออกมามันหน้าตาดีมาก ไม่มีการแปลงชื่อหรือเปลี่ยนรูปมาก
ทำให้เราสามารถเรียกใช้ได้ตรงๆ
groovy:000> f = RateStrategyParser.parse("minimum 1 min , nextcharge 6 sec : 10%")    
===> RateStrategy@34a02677
groovy:000> f
===> RateStrategy@34a02677
groovy:000> f.calc(24.00, 72)
===> 28.80

Related link from Roti

Friday, June 26, 2009

Dreyfus model

เมื่อวานได้มีโอกาสนั่งดู presentation หัวข้อ "Developing Expertise: Herding Racehorses, Racing Sheep" ของ Dave Thomas. ฟังแล้วประทับใจมากทั้งขำหัวเราะจนท้องแข็ง และปิ๊งกับเนื้อหาที่มีการใช้ metaphor เรื่องเด็กสองขวบทำให้ผม(ซึ่งมีลูกเล็กๆ 2 ขวบ) เห็นภาพชัดเจน

Theme ใน presentation ของ Dave(ที่ไม่ใช่ program dave ในหนังสือ ruby on rails ที่มีคนแปลมั่วๆไว้) กล่าวอ้างถึง Dreyfus model of skill acquisition ซึ่ง Stuart และ Hubert Dreyfus เสนอไว้เมื่อปี 1980 ว่า ในการเรียนรู้ทักษะใดๆก็ตาม มันมี 5 level ที่เราต้องผ่าน
  • Novice
  • Advance Beginner
  • Competent
  • Proficient
  • Expert
ที่น่าสนใจก็คือ Dave claim ว่าใน domain ใดๆก็ตาม พวกที่มีมากสุดก็คือ level 2 Advance Beginner, คำถามที่น่าสนใจก็คือ ทำไม? ทำไมคนส่วนใหญ่ถึงไปติดค้างอยู่ที่ระดับนั้น. บางคนก็เสนอขึ้นมาว่า เพราะเขาคิดไปเองน่ะสิว่า ระดับที่เขาอยู่นั้นเป็นระดับ 4. อันนี้ตรงกับกฎข้อที่ 1 ของ Dunning-Kruger effect ที่ว่า
Incompetent individuals tend to overestimate their own level of skill.
บางคนก็เสนอว่า ในการที่จะเลื่อนจากระดับ 2 ไประดับ 3 ได้นั้น เขาต้องการ mentor แต่จำนวนคนในระดับ 3,4,5 ไม่พอที่จะเป็น mentor ให้กับทุกคนในระดับ 2 (การเป็น mentor นั้นต้องอาศัยส่วนผสมหลายอย่างที่ลงตัว ไม่ใช่อยากจะเป็นก็เป็นได้)

Dave เขาถามผู้ฟังว่าอะไรคือความแตกต่างระหว่าง level 2 กับ level 3. ความแตกต่างที่เห็นชัดสุดก็คือ "Dependent" คนที่อยู่ระดับที่ 3 สามารถตัดสินใจได้ด้วยตัวเอง ต่างกับระดับที่ 2 ที่ต้องการให้มีคนบอกว่า ต้องทำอะไรบ้าง. สิ่งที่ตามมากับ Dependent ก็คือ "Risk". ฟังถึงตรงนี้แล้วตรงใจมาก คนส่วนใหญ่ไม่พร้อมจะเสี่ยง ทุกคนอยากอยู่ใน safety zone หรือ comfort zone

quote ที่ผมชอบสุดก็คือ ประโยคนี้
don't never ever let the expert choose your next architecture because they will choose the components that they are curious to see if it work.
ปล. 1 ในหนังสือ Pragmatic Thinking and Learning: Refactor Your Wetware มีบทที่ว่าด้วย Dreyfus Model อยู่บนหนึ่ง มีให้อ่าน free อยู่ครึ่งบทด้วย ใครสนใจไปตามอ่านได้ครับ Link (ผมอ่านแล้ว แล้วก็ลืมหมดแล้ว จนมาฟัง presentation นี้ก็เลยปิ๊งขึ้นมา)

ปล 2. ขอแสดงความเสียใจกับ Roti และ Opengis ที่ Opendream อนุญาติให้ผมเลือก components ตามใจชอบ (ตอนนี้พยายามยัด erlang ลงไปใน architecture อยู่)


Related link from Roti

Thursday, June 25, 2009

ไก่กับไข่ใน python OOP

วันนี้นั่งทำความเข้าใจกับ OOP ใน python
เนื่องจากไปเห็นว่า BaseModel ของ django มัน extend type
ก็เลยสงสัยว่าอะไรคือ object อะไรคือ type


# In Python, the __class__ attribute points to the type of an object

In [1]: object.__class__
Out[1]: <type 'type'>
# object มี type เป็น type

In [2]: type.__class__
Out[2]: <type 'type'>
# recursive structure นิ

#__bases__ attribute points to a tuple containing supertypes of an object
In [3]: object.__bases__
Out[3]: ()

In [4]: type.__bases__
Out[4]: (<type 'object'>,)
# เฮ้ยทำไม base ของ type เป็น object หล่ะ
# เจอปัญหาไก่กับไข่แล้ว

In [5]: isinstance(object, object)
Out[5]: True

In [6]: isinstance(type, object)
Out[6]: True


สรุปได้ว่า
  • <type 'object'> เป็น instance ของ <type 'type'>
  • <type 'object'> เป็น subtype ของ no object.
  • <type 'type'> เป็น instance ของตัวเอง.
  • <type 'type'> เป็น subtype ของ <type 'object'>.
<type 'type'> ก็คือ Metaclass ที่ดันเป็น subtype ของ instance ของตัวเอง

Related link from Roti

Friday, May 15, 2009

ย้าย commit ใน git

ปัญหาก็คือ branch ของผมมีหน้าตาเป็นแบบนี้

A deploy
/
D---E---F---G master

แต่ผมอยากย้าย commit G ไปอยู่ใน branch deploy
ให้มีหน้าตาแบบนี้

G--A deploy
/
D---E---F master

หลังจากนั่งทดลองอยู่นาน ก็พบว่า คำตอบนั้นง่ายนิดเดียว

git checkout master
git reset --hard HEAD^

Related link from Roti

Tuesday, April 21, 2009

pre-amp

หลวมตัวซื้อ mac มาใช้ ก็พบว่ามีหลายอย่างที่มันไม่ยอมทำให้เหมือนคนอื่น
เช่น mic input ของมันก็รับ input ที่ระดับ line-level แทนที่จะเป็น microphone-level
ซึ่งความแตกต่างของมันก็คือ ระดับของสัญญาณ
line-level มี voltage peek-to-peek ที่ระดับ millivolt
ส่วน mic-level มี voltage peek-to-peek ที่ระดับ volt

ครั้งจะไปหาซื้อ mic ของ apple มาใช้ ก็ดูเป็นการลงทุนที่ไม่คุ้มค่ายิ่งนัก
ต่อวงจรขยายเองน่าจะคุ้มกว่า
หลังจากเปิด google ไปสักพัก ก็พบวงจรนี้

เมื่อได้วงจรมา ก็ดองไว้อีกอาทิตย์กว่าๆ ก็มีอัศวินม้าขาวมาช่วยต่อวงจรให้
(อัศวินม้าขาวก็คือ "กอบ"-เพื่อนร่วมงานผม ผู้เชี่ยวชาญทั้งด้าน eclipse RCP + SWT และด้าน hardware)

ผลลัพท์ที่ได้



Related link from Roti

Monday, April 06, 2009

สรุปหนังสือจากงานหนังสือ 52

พอแก่แล้วก็เริ่มสนใจประวัติศาสตร์ ชุดนี้น่าสนใจตรงขอบเขตที่พยายามจะมองภาพรวมของภูมิภาค
  • เอเซียตะวันออกเฉียงใต้ในยุคการค้า ค.ศ. 1450-1680 เล่ม 1 ดินแดนใต้ลม (แอนโทนี รีด)
  • เอเซียตะวันออกเฉียงใต้ในยุคการค้า ค.ศ. 1450-1680 เล่ม 2 การขยายตัวและวิกฤติการณ์
เล่มนี้ของอ.เจตนา นาควัชระ  ผมสนใจหัวข้อ "สถาปัตยกรรมร่วมสมัยกับความคาดหวังของคนนอก"
  • เก่ากับใหม่ อะไรดี มนุษยศาสตร์ไทยในกระแสของความเปลี่ยนแปลง (เจตนา นาควัชระ)
สองเล่มนี้ พูดถึงประเด็นที่ไกล้เคียงกัน เล่มแรกผมสนใจการทำงานของจิตในเรื่องการคิด,  ส่วนเล่มสองเขาพูดประเด็นที่บอกว่า ระหว่าง trigger กับ action มันมี freedom อยู่, เล่มนี้ใช้แนวเขียนแบบ Story 
  • ทลายกับดักความคิด - Conceptual Blockbusting : A Guide to Better Ideas
  • พลิกคำถามเปลี่ยนชีวิต - Change Your Questions Change Your Life
เล่มนี้เป็นวรรณกรรมสำหรับเด็ก เป็นเรื่องของเด็กชาวเล (Note: ดูสิว่าจะอ่านแล้วรู้สึกทะแม่งเหมือนเรื่อง กะทิ หรือเปล่า, เรื่องกะทิกับผมมีปัญหากันตรงที่ ผมรู้สึกว่ามัน Fake) เล่มนี้กะว่าจะอ่านให้ลูกฟังก่อนนอน
  • บีตั๊ก ดาวดวงนั้นระหว่างน้ำกับฟ้า
ส่วนการ์ตูนเล่มนี้ คนเขียนเป็นคนมาเลเซีย ผมเคยเห็นลายเส้นของเขามานานแล้ว พอมีคนแปล ก็เลยตัดสินใจหยิบโดยไม่ลังเล
  • เด็กน้อยจากหมู่บ้าน The Kampung Boy ของ Lat.
ส่วนของสำนักพิมพ์มติชน คงไม่ต้องอธิบายมาก
  • เศรษฐศาสตร์แห่งชีวิต The Logic of Life
  • ประวัติศาสตร์โลกผ่านเกลือ Salt : A World History
  • ปฎิบัติการล่าไอคิว The Know-It-All
ของลูกชายได้มาสิบกว่าเล่ม เล่มที่ลูกชอบมากสุดคือเล่มนี้ เล่าตั้งแต่กำเนิดโลกยันมนุษย์ถ้ำ
  • โลกยุคก่อนประวัติศาสตร์ Prehistoric world 

Related link from Roti