Monday, November 22, 2010

javascript: undefined && null

Related link from Roti

Wednesday, November 10, 2010

ทบทวน drama เมื่อ 3 ปีก่อน

ขออนุญาติน้อง Deans4j นะ ที่ quote คำพูดบางอันมาออก
เพราะพี่ว่า "ความจริงมันเปิดเผยแล้ว ทุกคนเข้าใจแล้วว่าอะไรเป็นอะไร"

คุณ bow แกเขียน benchmark เทียบ Java, Python, Ruby
บังเอิญคุณ bow แก้มี domain ที่ใช้อยู่ในเรื่อง "scientific computation"
แล้วมีประสบการณ์ java ไม่พอที่จะ optimize
เจ้า deans4j ก็เลยเข้าไปจุดระเบิด

http://www.blognone.com/node/4385

มีเรื่องมีราว
เจ้าน้อง deans ก็ไปตั้งกระทู้ไว้ใน blog ตัวเอง

http://deans4j.wordpress.com/2007/04/12/against-idiot/

สมัยนั้นผมเป็นแฟน blognone และ markpeak
เห็นเจ้า deans4j aggressive ขนาดนั้น ทนไม่ได้ก็เลยไป comment ไว้แบบนี้


Dean4j, คุณมีความคิดด้านลบอยู่เต็มตัวเลยนะ

ผมลองเล่าเรื่องผมให้ฟังแล้วกัน
มีอยุ่ช่วงที่ผมรู้สึกว่า “คนเก่งๆหายไปไหนหมด”
มีอยู่วันหนึ่งมีคน post ในพันธ์ทิพย์ว่า
“ต้องการหาคนเป็น database ช่วยกันทำ project สนุกๆกัน”
ผมก็เลย mail เล่าไปว่าเคยทำอะไร อย่างไร
ปรากฎว่า mail ที่เขาตอบกลับมา นั้นทำให้ผมช็อค ไปเหมือนกัน
เขาบอกว่า เขาเกลียดคนชนิดผมมากเลย คนที่ภูมิใจในตัวเองสูง
ประโยคนั้นเป็นประโยคที่ดีมากเลย
ทำให้ต้องกลับมาทบทวนตัวเองใหม่
ซึ่งจากการทบทวนแล้ว ผมก็พบว่า สิ่งที่ผมเล่าอย่างธรรมดาๆนั้น
สำหรับคนอื่นแล้วมันดู arrogance เกินไป

ทำให้ผมต้องระมัดระวัง
วิธีการเขียนหรือเรื่องต่างๆ ที่อาจทำให้คนอื่นเข้าใจผิดในเจตนา
แต่ก็ไม่วายผิดผลาดอีก
อย่างเมื่อปลายปีก่อน apple เขาจัดอบรมฟรี
ผมก็ไปกับเขาด้วย
ก็ไปเจอพี่คนหนึ่ง อัธยาศัยดีมาก
กลับมาก็ mail คุยกัน
พี่เขาก็ถามว่า ปัจจุบัน ทำอะไรอยู่บ้าง
ผมก็เขียนเล่าเรื่องอย่างยาวเลย
ผลเป็นอย่างไรรุ้ไหม
พี่เขาเลิกติดต่อผมเลย
ซึ่งผมเดาได้ไม่ยากเลยว่า
มันต้องเกิดจากเนื้อความในจดหมาย
ซึ่งมันน่าจะมีส่วนผสมของความ Proud มากไปหน่อย
ทำให้คนอ่านรู้สึกว่าเราเป็นคน arrogance

บางทีความอัดอั้นตันใจของเรา, ความภูมิใจที่ถุกเก็บกดของเรา
ความรู้สึกไม่เต็มของเรา, ความรู้สึกไม่พอใจต่างๆ
มันเผลอสะท้อนออกไปทางสิ่งที่เราเขียน
โดยเราก็ไม่รู้ตัว

สำหรับ post ที่คุณตอบในบทความ “BowDerKliene”
ถ้ามองในแง่ fact แล้ว. เป็นการตอบที่ดีมาก
แต่ถ้ามองในแง่ของท่าทีแล้ว
มันมีน้ำหนักของการดูถูก และการท้าตีท้าต่อยมากไปหน่อย
ค่อนข้าง arrogance สูง

ส่วนเรื่อง FUD
อย่างไง FUD ก็เป็นสิ่งที่หลีกเลี่ยงไม่ได้
ตราบใดที่ คน ยังเป็น คน อยุ่
การชักจูงคนให้เห็นอีกมุม ไม่สามารถใช้วิธีปะทะได้หรอก
มันยิ่งทำให้เขาปฏิเสธมากขึ้นเท่านั้น


จากนั้น deans กับผม ก็เขียนจดหมายคุยกันหลังไมค์
นี่คือจดหมายที่ผมตอบเขา

ผมกับคุณมีส่วนหนึ่งที่เหมือนกัน ก็คือ เรามีความโกรธเป็นเจ้าเรือน
เรามีความคาดหวังสูงกับสิ่งรอบข้าง
ซึ่งมันมักจะทำให้เราผิดหวังกับโมโหอยู่บ่อยๆ

อย่าง mk บางทีตอนแรกคุณก็อาจจะรู้สึกว่า
เฮ้ย คนนี้ น่าจะเป็นเพื่อนกับเราได้ เขามีเป้าหมายบางอย่างที่ตรงกับเรา
แต่สุดท้ายคุณก็เริ่มพบว่า มันมีบางสิ่งที่มันไม่ตรงกันนัก
ความคิดเห็น อุดมการณ์ การแสดงออก

ส่วนเรื่องที่คุณผิดหวังกับวิธีคิดของเขา
ผมขอให้มองเขาเป็นคนธรรมดาเหมือนเรานี่แหล่ะ
มีการก้าวที่ผิดพลาด มี double standard (ทุกคนเป็นเหมือนกันหมด)
มีความขัดแย้งในสิ่งที่เชื่อกับสิ่งที่ทำ
สิ่งที่เขากำลังทำอยู่ ก็คือการเรียนรู้ชีิวิตของเขา

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

การปะทะเกิดขึ้นได้
แต่อย่าให้อารมณ์ร้ายๆ มามีผลต่อชีวิตเรา


อันนี้คือบางส่วนที่เขา ตอบกลับมา

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


เมื่อก่อนผมไม่เข้าใจที่ deans พูดหรอกนะ แต่ปัจจุบัน พฤติกรรมของ markpeak มันยืนยันตามคำพูดน้อง deans ทุกอย่าง


ตัดภาพกลับมาปัจจุบัน
หลังจากเรามี party "java เกรียน day"
ทุกอย่างที่ผ่านมา 3 ปี ก็ชัดเจนแล้วสำหรับผม

สุดท้ายผมก็ทำในสิ่งที่ผมแนะนำคนอื่นไม่ได้
อารมณ์ร้ายๆ ก็มามีผลต่อชิวิตผม

ศาสดาเจ็บใจมาก ตามมาเหน็บผมอีกแล้ว, พวกเกรียนใน net นี่ คงรู้สึกว่าพูดอย่างไรก็ได้เพราะใน net มันไกลตีน หรือเราควรจะลดระยะดี

หวังว่าสุดท้าย ตีนผมคงจะไม่ปะทะปากใครนะ

Related link from Roti

Tuesday, November 09, 2010

funciton form ใน javascript

หลังๆมา style การเขียน javascript ของผมมักจะตกไปอยู่ใน form แบบนี้
(โลกของ javascript ไม่มี class, ทุกอย่างเป็น object)
myService = {
sayHi: function(msg) {
this.output("-" + msg + "-");
},

output: function(msg) {
alert(msg);
}
}


หลังๆ javascript code ที่เขียนมันเริ่มใหญ่ (ถึงตอนนี้ก็ราวๆ 70000 บรรทัดแล้ว)
เวลา debug ก็เริ่มงงๆว่า ใครมันเป้นคนเรียก function ที่กูเขียนวะ

ใน webkit มันมี function ที่ชื่อ console.trace()
funciton นี้จะช่วย dump stacktrace ณ จุดที่เราใส่
ลองทดลองใส่ console.dump ใน code ข้างบน ก่อนตำแหน่ง alert

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

:( ได้ anonymous ออกมาหมดเลย ตกลงก็เลยไม่รู้เลยว่าใครเรียก

แต่ทุกปัญหามีทางออก วิธีแก้ก็คือ ปรับรูป form การประกาศ javascript เสียใหม่ เปลี่ยนเป็น
myService = {
sayHi: function sayHi(msg) {
this.output("-" + msg + "-");
},

output: function output(msg) {
console.trace();
alert(msg);
}
}


ทดลอง run ใหม่

ทีนี้ก็ต้องเลือกแล้ว จะเอา form เยิ่นเย้อ หรือเอาแบบที่เวลาไล่ stacktrace แล้วมันไล่ง่าย

Related link from Roti

Wednesday, October 27, 2010

ติดใจ repl ของ Clojure

ช่วงนี้ research หาลู่ทางรับงานจากอเมริกา
โจทย์ก็คือไป crawl ดึงข้อมูล จาก medicare.gov
ความยากอยู่ตรง site นั้นใช้ .net เขียนแบบ component based (stateful)
การจะได้ข้อมูลมา ต้องฝ่าด่านตอบคำถาม จำนวน 4-5 หน้าเข้าไปก่อน
ก็เลยต้องใช้ browser จริงๆเข้าไปดึงข้อมูล

หวยก็เลยออกมาที่ Selenium + WebDriver
แต่ครั้นจะเขียนด้วย java ก็ไม่สนุก
เขียนด้วย ruby/python ก็เคยเขียนแล้ว
ก็เลยเลือกใช้ Clojure

Lisp มันมีข้อดีอย่างหนึ่งก็คือ
มันมี REPL ทำให้เราสามารถเขียนโปรแกรมในลักษณะ incremental ได้
เขียนไป ทดลองไป ปรับแต่งไป

สภาพแวดล้อมที่เลือกใช้พัฒนาก็คือ
emacs + cake

ลองดูตัวอย่าง ตอนทดสอบได้ใน video นี้
(ดูช่วงต้นๆก็พอ, net/web มันช้า หลังๆหมดไปกับการรอมันโหลด
ใครว่า web ราชการไทยช้า web ราชการฝรั่งก็ช้าเหมือนกัน)



Updated: video upload ผ่านทาง blogger ได้ความละเอียดต่ำเหลือเกิน
ดูรูปประกอบแล้วกัน



ตัวอย่าง code ที่ใช้

(defn send-key [by txt]
(let [elm (.findElement *driver* by)]
(.sendKeys elm (into-array [txt]))))

(defn click [by]
(let [elm (.findElement *driver* by)]
(.click elm)))

(defn init-queue []
(def *queue* (LinkedBlockingQueue.)))

(defn start-thread [driver func]
(.start
(Thread.
(fn []
(loop []
(let [code (.take *queue*)]
(binding [*driver* driver]
(func code)))
(recur))))))

(defn fetch [code]
(go "https://www.medicare.gov/find-a-plan/questions/home.aspx")
(send-key (by-css-selector "div#zip-code-field > input") code)
(click (by-css-selector "input[type=submit][alternatetext=\"Find Plans\"]")))

Related link from Roti

Tuesday, October 26, 2010

เรียนรู้วิธีที่ ExtJS format date

ใน ExtJS เราใช้ function format ในการกำหนดรูปแบบการแสดงผลของ Date object
ลองดูตัวอย่าง

new Date().format("c")
"2010-10-26T08:35:30+07:00"

new Date().format("d/n/Y")
"26/10/2010"


คำถามที่น่าสนใจก็คือ เขาใช้วิธีไหนในการ implement function นี้

ลองเปิดไล่ดู code ใน file src/util/Date.js
เริ่มจาก object ที่ชื่อ formatCodes
formatCodes : {
d: "String.leftPad(this.getDate(), 2, '0')",
D: "Date.getShortDayName(this.getDay())", // get localised short day name
j: "this.getDate()",
l: "Date.dayNames[this.getDay()]",
N: "(this.getDay() ? this.getDay() : 7)",
S: "this.getSuffix()",
w: "this.getDay()",
z: "this.getDayOfYear()",
W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
F: "Date.monthNames[this.getMonth()]",
m: "String.leftPad(this.getMonth() + 1, 2, '0')",
M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
n: "(this.getMonth() + 1)",
....
}

คำถามที่ตามมาก็คือ ทำไมถึงเก็บ code ฝั่งขวาเป็น String

ลองไล่จาก top down บ้าง, โดยย้อยกลับไปดู function format ว่า code ข้างในเป็นอย่างไร
Date.prototype.format = Date.prototype.dateFormat;

dateFormat : function(format) {
if (Date.formatFunctions[format] == null) {
Date.createFormat(format);
}
return Date.formatFunctions[format].call(this);
},


จะเห็นว่า เมื่อได้รับ parameter เป็น format แล้ว, function มันจะพยายาม lookup หา function จากตาราง Date.formatFunctions ก่อน
ถ้าไม่เจอ ก็จะไปเรียก Date.createFormat เพื่อสร้าง function ให้ก่อน

ลองตามไปดู function createFormat บ้าง
code ที่เป็นพระเอกของเราก็คือ บรรทัดสุดท้าย
    createFormat : function(format) {
var code = [],
special = false,
ch = '';

for (var i = 0; i < format.length; ++i) {
ch = format.charAt(i);
if (!special && ch == "\\") {
special = true;
} else if (special) {
special = false;
code.push("'" + String.escape(ch) + "'");
} else {
code.push(Date.getFormatCode(ch))
}
}
Date.formatFunctions[format] = new Function("return " + code.join('+'));
},

จะเห็นว่ามัน loop ไปตามแต่ละ character ที่อยู่ใน format string ที่เราส่งให้
จากนั้นก็ไปเอาไป lookup ตาราง formatCodes ที่เราเห็นข้างบน ได้ค่ามาก็จัดการ แปะให้กลายเป็น anonymous function
แล้วก็เก็บ cache ไว้ใน Date.formatFunctions

ลอง dump Date.formatFunctions มาดู
Date.formatFunctions["d/n/Y"]
function anonymous() { return String.leftPad(this.getDate(), 2, '0')+'/'+(this.getMonth() + 1)+'/'+this.getFullYear();
}


พอเห็น code แบบนี้แล้ว การเพิ่มวันที่ไทยเข้าไปเองก็ไม่ยากแล้ว

Related link from Roti

Sunday, August 08, 2010

export canvas to image

เมื่อวานกลุ่ม Creative Hacker ได้มีโอกาสไปนั่งคุยกันเรื่อง HTML5. ประเด็นหนึ่งที่ผมสนใจติดตามต่อ ก็คือเรื่องที่เราสามารถ export canvas ของเราออกมาเป็น Image ได้เลย

สิ่งที่สงสัยก็คือ, image ที่มัน export ออกมามันอยู่ในรูปแบบอะไร. กลับมาบ้านก็เลยใช้ chrome ช่วยเฉลย

$('#canvas')[0].toDataURL('image/png')

"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAABkCAYAA..."

Update 2010-08-08 10:03

ใน firefox ถ้าต้องการให้ save ลงเป็น file เลยให้ทำดังนี้

var data = $('#canvas')[0].toDataURL('image/png');
data.replace('image/png', 'image/octet-stream');
document.location.href=data;

Related link from Roti

Monday, July 12, 2010

"new" operator in javascript

ช่วงนี้ได้มีโอกาสสอน javascript ก็เลยได้มีโอกาสทบทวนหลักการพื้นฐานของมัน
ปกติผมมักจะใช้อย่างเดียว ไม่เคยอ่าน spec หรือศึกษารากฐานของมัน (คิดว่าหลายๆคนก็คงเป็นเช่นนี้เหมือนกัน)

วันนี้จะพูดถึงประเด็นหนึ่งที่น่าสนใจก็คือ เรื่อง new operator

ลองดู code นี้ ที่แสดงการสร้าง object ใหม่ๆด้วย new operator
function Person(name) {
this.name = name;
}

p1 = new Person('pok');

โปรแกรมเมอร์ที่มาจากสาย OOP ทั้งหลายเห็น code นี้แล้วมักจะรู้สึกทะแม่งๆ ตรงที่ว่า
เหตุใด function ธรรมดา ถึงเอาไปสร้าง object ได้
(ใน javascript, function ที่ใช้นำไปสร้าง object เราเรียกมันว่า constructor)

สาเหตุที่เราสงสัยแบบนี้ ก็เพราะว่า paradigm ของเราผูกอยู่กับ โลก OOP แบบ class กับ instance
แต่ใน javascript นั้นโลกของมันไม่มี class, ทุกอย่างในโลกของ javascript คือ Object (มี object อยู่ type เดียวเท่านั้น)
สิ่งที่ต่างกันก็คือ constructor ที่ใช้สร้าง Object นั้นๆ
เช่น date object ก็เกิดจาก constructor ที่ชื่อ Date

เพื่อให้เข้าใจถึง concept constructor เราลองดูขั้นตอนภายในของ new operator บ้างว่า จริงๆแล้วมันทำงานอย่างไร
เริ่มด้วยการ inspect เจ้า p1 ที่สร้างจาก code ข้างบน



ทีนี้เราจะลองสร้าง p2 ที่มี structure เหมือนกับ p1 แต่ไม่ใช้ new operator ในการสร้าง
เริ่มด้วย new Object ธรรมดาขึ้นมาก่อน (อย่างที่บอก ใน javascirpt ทุกอย่างคือ Object ธรรมดาๆ)
p2 = new Object()

ลอง inspect ดู จะเห็นว่า มันคือ object เปล่าๆ ซึ่งมี __proto__ ชี้ไปยัง root object



สิ่งที่เราต้องทำก็คือ เปลี่ยน __proto__ reference ให้ชี้ไปยัง prototype ของ Person
(function ทุก function ใน javascript จะมี property ที่ชื่อ prototype เกิดขึ้นโดยอัตโนมัติ)
p2.__proto__ = Person.prototype;


สุดท้ายก็ทำการเรียกใช้ function Person โดยเปลี่ยน scope ของ this ให้กลายเป็น p2 object ของเรา
Person.apply(p2, ['pok']);


ลอง inspect p2 ดู จะเห็นว่าหน้าตามันเหมือน p1 ที่สร้างจาก new Person(...) แล้ว



Note: เราอาจเรียก function ที่เราใช้กับ new operator ว่า Template function ก็ได้
เพราะหน้าที่มันก็คือการ assign property ให้กับ object เปล่าๆที่ถูกสร้างขึ้นมาโดย new operator

Related link from Roti

Thursday, April 29, 2010

Good Designer

มีคนตั้งคำถามใน reddit (ดูแล้วก็เหมาะที่เราจะใช้ถามตัวเองด้วย)


เขาเป็นใคร และทำไมถึงถามคำถามนี้


I'm a developer, not a designer - I know my way around HTML and CSS. I also have a passing knowledge of Gimp and Inkscape. The technical stuff I don't have a problem with.

However, when I try to design a site myself the result is rather boring and minimalist. It's valid, accessible, SEO-friendly and so on, but it lacks originality and doesn't really stand out.


มีคำตอบมากมายหลายแบบ (แน่นอน คำถามมันปลายเปิดขนาดนั้น)

บางคนก็ว่า

A lot of design is obsessing over the tiniest details that have a larger impact on the overall "feel" of the design than you might have anticipated.


อันนี้ก็น่าสนใจ

I think if I could sum up effective web design today in a single word, it would be “subtlety”.
Most boring designs have no subtlety, because there isn’t really any detail there.
However, most really bad designs also have no subtlety, because everything is overdone.


(อะไรคือ subtlety?, ถ้าสงสัยแล้ว อย่าปล่อยให้มันลอยผ่านไปโดยไม่ค้นหา)



แล้ว Artistic หล่ะ

Artistic ability is a prerequisite to becoming a great graphic designer. That said, artistic ability is a skill.


skill คือ ทักษะ, ทักษะเกิดจากอะไร

it's practise, practise, practise like painting, because every project is different, every client is different and you can't afford to ever stop learning. In a roundabout way, for example, being a good photographer can be more important in design, than being good at photoshop.

It's about your ideas, ideas you can only get by having a massive arsenal of previous experience (personal is good, professional is better, you just need to have finished a lot of work). If you don't have this, you don't know what is possible and what you are capable of.


Related link from Roti

Thursday, April 22, 2010

Evolution of programming languages

อ่านบทความ Functional Logic Programming ใน Communications of the ACM แล้วชอบที่เขาเกริ่นลำดับพัฒนาการของ programming language

mnemonic instructions กับ symbolic labels ใน Assembly เกิดขึ้นเพื่อที่จะซ่อน macine code กับ address
arrays and expression ใน Fortran เกิดขึ้นเพื่อซ่อน registers
structured programming เกิดขึ้นเพื่อซ่อน goto กับ jump labels
encapsulate ใน OOP เกิดขึ้นเพื่อซ่อน representation of data

declarative language(เช่น functional programming) เกิดขึ้นเพื่อซ่อน ลำดับการทำงาน, assignment และ control statements

Related link from Roti

Wednesday, April 21, 2010

update เส้นทางขี่จักรยาน

เห็นเก่ง post เส้นทางขี่จักรยานแล้วฉุกใจคิด
เดิมคิดว่า ระยะทางจากบ้านไป opendream น่าจะประมาณ 5 กิโล
วันนี้ก็เลยลอง plot แผนที่ดู



สรุปว่าจากบ้านไป opendream = 10 KM
มิน่าวันนี้ทำไมรู้สึกเพลียๆ (อากาศมันร้อนด้วย)

ปล. เส้นสีฟ้าคือ ไปกลับ บ้าน-บริษัท Ingres
เส้นสีแดงคือ เส้นกลับถ้าใช้เสือหมอบ (ถ้าใช้เสื้อภูเขา กลับเส้นฟ้า)
เส้นสีเขียว คือ ไปกลับ บ้าน-opendream
ทั้งฟ้าและแดง ระยะทางเท่าๆกัน = 5 KM

Related link from Roti

Tuesday, March 23, 2010

ชอบแบบไหน


สองรูปข้างบนแสดงการต่อวงจรแบบเดียวกัน
คำถามคือ "แวบแรกที่เห็น คุณชอบแบบไหน?"


สมมติฐานของผมก็คือ จากคำตอบนั้นน่าจะคาดเดาได้ด้วยว่า คุณน่าจะชอบ programming language ตัวไหน.

Related link from Roti

Tuesday, March 16, 2010

วาดรูป-ฝึกสังเกต

ช่วงนี้ลูกๆปิดเทอม เลยได้ฤกษ์พากันไปเที่ยว แน่นอนว่าหน้าร้อนอย่างนี้ จะมีที่ไหนดีไปกว่าทะเลอีก ระหว่างทางต้องผ่านพิพิธภัณฑ์สัตว์น้ำหว้ากอด้วย แน่นอนว่าไม่แวะไม่ได้

ระหว่างเดินดูสัตว์น้ำทั้งหลาย ผมก็พยายามฝึกให้ลูกมองเห็นสิ่งเล็กๆน้อยๆต่างๆ โดยใช้วิธีตั้งคำถาม หรือให้เทียบเคียงกับตัวเอง เช่น พอเจอเจ้าปลากลับหัว ก็ชวนกันเดินด้วยมือแทนเท้า

ไปเที่ยวคราวนี้ลูกเริ่มโตขึ้น(5 ขวบ) ก็เลยเพิ่มกลยุทธ์การเรียนรู้ด้วยการวาดรูป
วิธีการก็คือ พ่อเป็นคนวาด ส่วนลูกทำหน้าที่บอกรายละเอียดว่าถูกต้องไหม นับจำนวนว่าครบไม่ครบ

อันนี้คือ เต่าทะเล กับ ปลาดาว ที่วาดกัน ลูกต้องนับขาปลาดาว กับนับจำนวนลวดลายของเต่า

From 7-14 bann grood,huahin

Related link from Roti

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