Friday, March 07, 2008

snail-shell

เมื่อวานนั่งเขียนโจทย์เกี่ยวกับ shell-snail ใน codenone ด้วย Haskell
แต่ใน codenone มันทำ syntax highlight ไม่ได้
ก็เลยมาขอฉายซ้ำใน blog อีกที

เริ่มจากโจทย์ก่อน
ให้เขียนโปรแกรมที่รับค่า N และนำมา plot graph ให้ได้ตามนี้

กรณี N=3
***
*
***

กรณี N=4
****
*
* *
****

กรณี N=5
*****
*
*** *
* *
*****

กรณี N=6
******
*
**** *
* * *
* *
******


คิดจะเขียนโปรแกรมด้วย Haskell ก็ต้องคิดแบบ recursive เป็นหลัก
จะเห็นว่าที่ dimension N ใดๆ จะมี sub picture ที่ (N-2) ซ้อนอยู่ในนั้นด้วย
แต่จะซ้อนอยู่ในลักษณะกลับหัวกลับหาง (คือ flip ทาง horizontal ที่หนึ่งก่อน แล้วค่อย flip ทาง vertical อีกที)

ลองดูรูป N=6 จะเห็นว่าเกิดจาก

N=4

****
*
* *
****

กลับหัวกลับหางเป็น

****
* *
*
****

แล้วนำไปซ้อนกับรูป พื้นฐาน ที่ N=6

******
*
*
*
*
******


ดังนั้น นิยามของรูปของเราก็คือ

รูป N = (รูป (N-2) กลับหัวกลับหาง) ซ้อนกับ รูปพื้นฐาน N

เขียนเป็น function ได้ว่า

pic n = (flip_all (pic n-2)) `over` base n


เนื่องจากโปรแกรมเราเป็น recursive ดังนั้นมันก็ต้องมี รูปที่เป็นจุดเริ่มต้นให้มันด้วย
ซึ่งในกรณีนี้ก็คือ N=1 และ N=2
pic 1 = ["*"]
pic 2 = ["**"," *"]
pic n = (flip_all (pic n-2)) `over` base n

นิยามของ base n ก็ง่ายๆนั่นคือ
เอา '****' มาประกบหัวและหางเข้ากับ ' *'
โดยมีความกว้างและความสูงเท่ากับค่า N
-- base 3 = ["***",
-- " *",
-- "***"]
--
-- base 4 = ["****",
-- " *",
-- " *",
-- "****"]
--
-- for n = 4
-- hbar = "****"
-- rbar = " *"
--
base n = [hbar] ++ (replicate (n-2) rbar) ++ [hbar]
where hbar = replicate n '*'
rbar = (replicate (n-1) ' ') ++ ['*']

function ถัดไปก็คือ flip_all
ซึ่งเกิดจาก flip_vertical compose กับ flip_horizontal
--
--render $ flip_horizontal $ pic 4
--
-- "****"
-- "* "
-- "* *"
-- "****"
flip_vertical = reverse
flip_horizontal = map reverse
flip_all = flip_vertical . flip_horizontal

function ที่ดูจะยุ่งยากสุด ก็คือ function ที่ใช้วางรูปซ้อนกัน
เพื่อความง่ายในการซ้อน เราเลยเพิ่ม helper function ที่ใช้ในการปรับขนาดรูปให้เท่ากันเสียก่อน
ก่อนที่จะนำมาซ้อนกัน
-- patch (base 3) 5 = ["     ",
-- " ",
-- "*** ",
-- " * ",
-- "*** "]
--
patch xs n = replicate diff blankline ++
[ x ++ (replicate diff ' ') | x <- xs]
where blankline = replicate n ' '
diff = n - (length xs)


-- ["****", [" ", ["****",
-- " *", over "*** ", => "****",
-- " *", "* ", "* *",
-- "****"] "*** "] "****"]
--
over xs ys = zipWith over' xs ys
where over' linex liney = zipWith over'' linex liney
over'' x y | x == ' ' = y
| otherwise = x

ปรับนิยาม pic ของเราให้ใช้ patch function ช่วยปรับขนาดก่อนวางทาบกัน
pic 1 = ["*"]
pic 2 = ["**"," *"]
pic n = (patch (flip_all $ pic (n-2)) n) `over` base n


ส่วนการ render นั้นมันมีเรื่อง IO มาเกี่ยวข้องด้วย
ก็ต้องใช้ function พวก mapM (M มาจาก Monad) เข้ามาช่วย
render n = do 
print $ "N = " ++ (show n)
mapM print (pic n)


ลองสั่ง run ดู
*Main> mapM render [1..8]
"N = 1"
"*"
"N = 2"
"**"
" *"
"N = 3"
"***"
" *"
"***"
"N = 4"
"****"
" *"
"* *"
"****"
"N = 5"
"*****"
" *"
"*** *"
"* *"
"*****"
"N = 6"
"******"
" *"
"**** *"
"* * *"
"* *"
"******"
"N = 7"
"*******"
" *"
"***** *"
"* * *"
"* *** *"
"* *"
"*******"
"N = 8"
"********"
" *"
"****** *"
"* * *"
"* * * *"
"* **** *"
"* *"
"********"

Related link from Roti

Wednesday, March 05, 2008

Yak shaving

"Yak shaving" is a programmer's slang term for the distance between a task's start and completion and the tangential tasks between you and the solution. If you ever wanted to mail a letter, but couldn't find a stamp, and had to drive your car to get the stamp, but also needed to refill the tank with gas, which then let you get to the post office where you could buy a stamp to mail your letter—then you've done some yak shaving.


อันนี้เป็นอีกนิยามหนึ่ง

Any seemingly pointless activity which is actually necessary to solve a problem which solves a problem which, several levels of recursion later, solves the real problem you're working on.


สำหรับผม บางครั้ง Yak shaving ีมันก็สนุก จนอดใจที่จะไม่ลงไปแตะไม่ได้

ช่วงนี้ใช้ python เขียน script สำหรับ deploy java applicaion บน cluster อยู่
มันมี Yak shaving น้อยดี

Related link from Roti

Wednesday, February 27, 2008

แอบดู GWT code

เมื่อวานผมเขียน code GWT ในส่วนที่เกี่ยวกับ java.util.Date
แล้วเกิดสงสัยว่ามัน implement อย่างไร ก็เลยลอง download source code ของ GWT มาดู
เปิดดูแล้วก็ร้อง 'ฮ่อ'

ลองดูบางส่วนที่ผมตัดมาให้ดู

/**
* Represents a date and time.
*/

public class Date implements Cloneable, Comparable<Date>, Serializable {

public Date() {
init();
}

private native void init() /*-{
this.jsdate = new Date();
}-*/
;


public native int getDate() /*-{
return this.jsdate.getDate();
}-*/
;

public native int getDay() /*-{
return this.jsdate.getDay();
}-*/
;

public native int getHours() /*-{
return this.jsdate.getHours();
}-*/
;

public native String toGMTString() /*-{
var d = this.jsdate;
var padTwo = @java.util.Date::padTwo(I);
var month =
@java.util.Date::monthToString(I)(this.jsdate.getUTCMonth());

return d.getUTCDate() + " " +
month + " " +
d.getUTCFullYear() + " " +
padTwo(d.getUTCHours()) + ":" +
padTwo(d.getUTCMinutes()) + ":" +
padTwo(d.getUTCSeconds()) +
" GMT";
}-*/
;

private static String padTwo(int number) {
if (number < 10) {
return "0" + number;
} else {
return String.valueOf(number);
}
}
}

งามดีเหมือนกัน ส่วนที่ cross กันระหว่าง javascript กับ java (ก่อนที่จะแปลงเป็น javascript)

ที่น่าสนใจก็คือ ตอนที่ฝั่ง javascript call มาที่ฝั่ง java
จาก code ข้างบนลองดูวิธีการ call method padTwo

// เริ่มด้วยการอ้างถึง method ที่ต้องการ
// จะเห็นว่ามันใช้ signature ของ method ในการอ้างถึง method ที่ต้องการ
// สิ่งที่ return กลับมาก็น่าจะเป็น javascript function

var padTwo = @java.util.Date::padTwo(I);
...
padTwo(d.getUTCHours()) + ...
...

Related link from Roti

Wednesday, February 20, 2008

GWT กับ Spring

ผม search หาวิธี map GWT เข้ากับ SpringFramework มาพักใหญ่แล้ว
ส่วนใหญ่ที่เห็นก็จะเป็นการเขียน DispatcherServlet ของตัวเองขึ้นมา
เช่น GWT-SL
วิธีการของ GWT-SL จะเป็นดังนี้
  • config servlet ใน web.xml
      <servlet>
    <servlet-name>rpc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
    <servlet-name>rpc</servlet-name>
    <url-pattern>*.rpc</url-pattern>
    </servlet-mapping>

  • config spring context โดยใช้ GWTHandler เข้ามาเป็นตัว wrapper
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">

    <beans>
    <bean id="urlMapping" class="org.gwtwidgets.server.spring.GWTHandler">
    <property name="mappings">
    <map>
    <entry key="/wcfb110_jobService.rpc" value-ref="jobService"/>
    </map>
    </property>
    </bean>

    <bean id="jobService" class="wcf.web.fin.gwt.wcfb110.server.JobServiceImpl"/>
    </beans>
    </pre>


ดูก็ง่ายดีแล้ว แต่มาวันนี้เจอตัวอย่าง source code ของ Interface21(เจ้าพ่อ spring) ซึ่ง config spring กับ GWT
(โปรดระวัง เห็นนามสกุลเป็น .zip แต่จริงๆแล้วมันเป็นพวก .tgz)
เห็นแล้วต้องร้อง Wow, ทำไมมัน simple อย่างนี้

เขาใช้วิธีนี้
เริ่มด้วย web.xml ที่ทำเหมือนกันกับข้างบน
แต่ ตัว spring context เขาเปลี่ยนเป็นแบบนี้
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
>

<context:component-scan base-package="gwt.server" />

</beans>

แล้วตัว GWT Servlet ของเราก็ใส่ annotation เข้าไปแบบนี้
@Controller
public class NoteServiceImpl extends RemoteServiceServlet implements NoteService, ServletConfigAware {

public void setServletConfig(ServletConfig servletConfig) {
try {
init(servletConfig);
} catch (ServletException e) {
throw new IllegalStateException("Cannot initialise Controller.", e);
}
}

@RequestMapping("/gwt.Example/NoteService.rpc")
public void doRpc(HttpServletRequest request, HttpServletResponse response)
throws Exception {
doPost(request, response);
}

public Note loadNote(String name) {
return new Note(name, "This Note named " + name + " was constructed on the server");
}
}

ง่ายขึ้นเยอะ ลด dependency ของพวก wrapper ไปได้
แต่ข่าวร้ายสำหรับผมก็คือ annotation พวกนี้เป็น feature ของ spring 2.5.x
โชคร้ายที่โปรเจคผมยังใช้ spring 2.0.x อยู่เลย

Related link from Roti

Thursday, February 14, 2008

Git กับการ revert

ใน svn เวลาเราทำอะไรผิดผลาด แล้วเราอยากย้อนกลับเนื้อหา file
มันก็มี case อยู่แค่ 2 case คือ
  1. file ยังไม่ถูก commit
    อันนี้ง่ายเลย แค่ใช้ svn revert ก็จบ
  2. file ถูก commit เข้าไปแล้ว
    อันนี้ก็ง่าย ก็แค่ revert โดยระบุ revision ที่เราต้องการ

    svn update -r REVISION_ID



ส่วนใน git นั้น มันไม่ง่ายเหมือน svn เพราะว่า มันมี state มากกว่า svn
โดย git มันจะมี state ของ file ได้ดังนี้
  • สถานะปกติ ยังไม่ถูกแก้ไข
  • ถูกแก้ไขแล้ว แต่ยังไม่ได้ add เข้า index (index ของ git เปรียบเสมือน buffer area ที่รอการ commit)
  • add เข้า index แล้ว แต่ยังไม่ได้ commit


จะเห็นว่ามี state ที่ content ค้างอยู่ใน index แต่ยังไม่ถูก commit เพิ่มเข้ามา(เมื่อเทียบกับ svn)
ดังนั้นถ้าเราจะ revert มันก็ต้องคิดว่าจะ revert ขั้นไหน เช่น

  1. file นั้นยังไม่ได้ถูก add เข้า index กรณีนี้ใช้คำสั่ง
    git checkout -- FILENAME

  2. file นั้นถูก add เข้า index แล้ว แต่ต้องการเอาออกมาจาก index
    git reset

    (state จะกลับไปเหมือนในข้อ 1)
  3. file นั้นถูก commit ไปแล้ว แต่ต้องการย้อนกลับออกมาสู่สถานะที่ file นั้นอยู่ใน index
    git reset --soft COMMIT_ID

  4. file นั้นถูก commit ไปแล้ว แต่ต้องการย้อนออกมาสู่สถานะก่อนการ add เข้า index
    ให้ทำคำสั่งในขั้น 3 แล้วต่อด้วยคำสั่งในขั้น 2
  5. file นั้นถูก commit ไปแล้ว และต้องการย้อนกลับมาสู่ version ก่อนหน้านั้น โดยเก็บ track การ revert ไว้ใน history ด้วย
    git revert COMMIT_ID

  6. file นั้นถูก commit ไปแล้ว แต่ต้องการย้อนไปสู่ version(ใดๆ) ก่อนหน้านั้น โดยไม่ทิ้งร่อยรอย
    git reset --hard COMMIT_ID



Note: ผมใช้คำว่า file ซึ่งไม่ถูกต้อง จริงๆต้องใช้คำว่า content (อ่านได้ใน "Git อีกทีน่า")
แต่เพื่อให้สื่อสารได้ง่ายขึ้น ขออนุญาติใช้คำว่า file

Related link from Roti

Wednesday, February 13, 2008

Open class

Ruby

>> class Integer
>> def fact
>> (2..self).inject(1) {|acc,v| acc * v}
>> end
>> end
=> nil
>> 7.fact
=> 5040


Grovvy

groovy:000> Integer.metaClass.fact = {   
groovy:001> delegate == 1 ? 1 : delegate * (delegate-1).fact()
groovy:002> }
===> groovysh_evaluate$_run_closure1@89ff97
groovy:000> 7.fact()
===> 5040


Scala

scala> class IntExt(v:Int) {             
| def fact() = {
| (2 to v).foldLeft(1) (_ * _)
| }
| }
defined class IntExt

scala> implicit def FactExt(v:Int) = new IntExt(v)
FactExt: (Int)IntExt

scala> 7 fact
res33: Int = 5040


เจ้า scala นี่แปลกตรงมันใช้หลักการ Implicit conversions
อย่างประโยค
7 fact จริงๆคือ
7.fact()
แต่ method fact มันไม่มีใน class Int
ดังนั้น compiler จะดูว่ามี Implicit conversions rule อะไรอยู่บ้าง
ในกรณีนี้ ก็คือจะแอบแปลงให้เป็นแบบนี้
(new IntExt(7)).fact()

Related link from Roti

Friday, February 08, 2008

Here documents

พึ่งรู้ว่า Here documents ของ Ruby ทำอะไรแปลกๆแบบนี้ได้ด้วย
puts 'Produce', '-------', <<PRODUCE, 'Dairy', '-----', <<DAIRY, '* Organic'
1. Strawberries*
2. Blueberries

PRODUCE

1. Yogurt
2. Milk*
3. Cottage Cheese

DAIRY

ใน rails (postgresql adapter) ก็มีใช้เหมือนกัน Charlie เขาชี้ให้ดู

Related link from Roti

Thursday, February 07, 2008

haskell -> python -> ruby -> ...

วันนี้เจอโปรแกรม haskell เขียนโดย sigfpe


def q(a,b,c):print b+chr(10)+'q('+repr(b)+','+repr(c)+','+repr(a)+')'
q("def q(a,b,c):print b+chr(10)+'q('+repr(b)+','+repr(c)+','+repr(a)+')'","def e(x) return 34.chr+x+34.chr end;def q(a,b,c) print b+10.chr+'main=q '+e(b)+' '+e(c)+' '+e(a)+' '+10.chr end","q a b c=putStrLn $ b ++ [toEnum 10,'q','('] ++ show b ++ [','] ++ show c ++ [','] ++ show a ++ [')']")

ทายสิว่ามันทำอะไร
ลอง copy ไปใส่ file ชื่อ x.hs ดู
แล้วลอง run แบบนี้

$ runhaskell x.hs > x.py
$ python x.py > x.rb
$ ruby x.rb > y.hs

$ runhaskell y.hs > y.py
$ python y.py > y.rb
$ ruby y.rb > z.hs

... (ทำไปเรื่อยๆ จนกว่าจะเบื่อ)

Related link from Roti

Friday, February 01, 2008

วันโชคร้ายของจักรยาน

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


View Larger Map

ขาไปทำงาน ส่วนใหญ่จะขี่แบบเรื่อยๆเฉื่อยๆ เพราะพึ่งกินอิ่ม
ส่วนขากลับนี่ ทางมันตรง ก็เลยใส่เต็มที่ ลมออกหูทุกวัน (เหนื่อย)

ส่วนวันนี้ ค่อนข้างโชคร้ายหน่อย
ออกจากบ้านมาได้หน่อยเดียว ยางดันแตก
ก็เลยต้องเข็นกลับไปปะยางที่บ้าน
ดูบาดแผลแล้วเกิดจากเศษแก้วแตก

หลังจากปะเสร็จก็ออกมาอีกครั้ง
ปั่นมาถึงแถววังหิน
คราวนี้ โซ่ขาด
โชคดีได้ร้านซ่อมมอเตอร์ไซด์แถวนั้น จัดต่อโซ่ให้

ปล. เมื่อต้นอาทิตย์ ขี่จักรยานไปเจอเจ้าปั้นเหน่งกำลังข้ามถนน
สอบถามได้ความว่า codegent ตั้งอยู่แถวนี้ด้วย
เห็นชุดทำงานมันแล้ว ต้องอิจฉา บริษัทอะไรใส่เสื้อกล้ามโชว์รักแร้ไปทำงานได้ด้วย

Related link from Roti

Monday, January 28, 2008

Y combinator in BarcampBangkok

คำเตือน
post นี้อาจทำให้เกิด stack overflow ในสมองคนอ่านได้ (เหมือนที่เกิดกับคนเขียนไปแล้ว)

กลับจาก BarcampBangkok ด้้วยสมองอันหนักอื้ง
สิ่งหนึ่งที่ยังติดใจไม่หายก็คือ source code ของ Haskell หน้าตาแบบนี้
(จาก presentation เรื่อง Yoda in Haskell ของ Ben)
fix f = let x = f x in x

โชคดีที่เหลือบมองเห็น comment ที่ Ben เขาเขียนเหนือ function นี้ มันมีคำว่า Y combinator อยู่ด้วย

เรื่อง Y combinator เคยผ่านตามาหลายครั้งแล้ว แต่ไม่เคยทำความเข้าใจได้สักที
วันนี้เลยได้ฤกษ์การพยายามครั้งใหม่

จากการ search google ก็พบว่า code ข้างบนนั้น มันมีชื่อเรียกว่า
Fixed point combinator
หลักการมันประมาณนี้

เริ่มที่คำว่า Fixed point ก่อน
fix point ก็คือ ค่าที่ทำให้ผลลัพท์ของ function มีคุณสมบัติแบบนี้
f(x) = x

ในทางคณิตศาสตร์ สมมติเรามี function อยู่อันหนึ่งซึ่งมี definition ดังนี้
f(x) = x^2

ค่า fixed point คือ function นี้ก็คือ 0 และ 1
เพราะ 0^2 == 0 และ 1^2 = 1

ใน functional language ทุกอย่างเป็น function
ดังนั้น เมื่อมี function ก็แสดงว่า มันก็ต้องมี fixed point ได้เหมือนกัน
ลองนึกถึง higher-order function f ที่รับ parameter เป็น function
มันก็ควรจะมี fixed point function p ซึ่ง
f(p) = p


Y combinator ก็คือ function g ซึ่งรับ parameter เป็น function f
ผลลัพท์ที่ได้ ก็คือ function p ซึ่งเป็น fixed point ของ function f
p = g(f), f(p) = p

ลองอ่านคำอธิบาย contract ของ Y combinator เทียบกับ function ข้างบน
Give me myself and a higher order function f, and I’ll return to you a function that is a fixed point of f.


ถ้าลองแทน function ข้างบนไปมา ก็จะได้
f(g(f)) = g(f)

อ้าจ้องไปจ้องมา แล้วมันก็คือ let x = f x in x นั่นเอง

ประเด็นหลักๆของไอ้ Fix ก็คงจะอยู่ที่เรื่อง Lambda expression
เนื่องจากเราไม่สามารถเขียน recursive function ในรูป lambda expression ได้
ใครอยากรู้ลองอ่านไอ้นี่เอาแล้วกัน
http://en.wikipedia.org/wiki/Lambda_abstraction#Recursion

ลองดูตัวอย่าง่ factorial บ้าง
ปกติเราเขียน factorial อย่างนี้

fact n = if n == 0 then 1 else n * fact (n-1)


ลองปรับ form ให้เป็น lambda expression โดยสร้าง function genfact ขึ้นมา

genfact f n = if n == 0 then 1 else n * f(n-1)

ลอง apply fix เข้าไป

fix genfact 3 = fact (fix genfact) 3
= 3 * ((fix genfact) 2)
= 3 * (genfact (fix genfact) 2)
= 3 * 2 * ((fix genfact) 1)
= 3 * 2 * 1 * ((fix genfact) 0)
= 3 * 2 * 1 * 1
= 6


พอแล้ว stack ผม overflow แล้ว

Related link from Roti

Tuesday, January 22, 2008

Mixin - Thin & Think interface

เห็น Bow เขียนเรื่อง Mixin กับ python ไป
ก็เลยคิดว่าจะลองขยาย Idea ของเรื่องนี้ดู

ในหนังสือ Programming in Scala ที่ผมกำลังอ่านอยู่
เขาพูดถึง Mixin ในประเด็น
Thin versus Thick interfaces

Thick interface คือ interface ที่อุดมสมบูรณ์ เพรียบพร้อมไปด้วย method, พร้อมจะให้ developer เด็ด method ที่ต้องการไปเชยชม
ส่วน Thin interface คือ interface ที่มี minimum method
อยากได้อะไรที่เกินกว่าที่จัดหาให้ ก็ออกแรงเขียน code เอาหน่อย
เมื่อเทียบ ruby กับ java แล้ว
java จะออกไปในแนว Thin interface
ส่วน ruby จะออกไปในแนว Think interface

ในหนังสือเขายกตัวอย่างง่ายๆคือ
interface Ordered {

}

ถ้าเป็น Thin interface แล้วหล่ะก็
method ที่ควรจะมี ก็น่าจะมีแค่นี้ก็พอแล้ว
interface Ordered {
/**
* return -1 if this
< that
* return 0 if this == that
* return 1 if this
> that
*/

public int compare(Ordered that);
}

ถ้า developer อยาก check เงื่อนไข <= ก็ให้ทำแบบนี้เอาเอง
if (this.compare(that) <= 0) {
...
}

แต่ถ้ามองแบบ Think interface ก็ควรจะเป็นแบบนี้
// สมมติว่า java define operator ได้
interface Ordered {
public boolean <= (Ordered that);
public boolean < (Ordered that);
public boolean > (Ordered that);
public boolean >= (Ordered that);
}


Mixin ก็เข้ามามีบทบาทตรงนี้แหล่ะ
กรณี scala เราเขียน Mixin โดยใช้ keywork ว่า Trait ได้ดังนี้
trait Ordered[a] {
// abstract method, class ที่ include mixin นี้ ต้อง implement method นี้
def compare(that: a): Int
def < (that: a): Boolean = (this compare that) < 0
def > (that: a): Boolean = (this compare that) > 0
def <= (that: a): Boolean = (this compare that) <= 0
def >= (that: a): Boolean = (this compare that) >= 0
}

จะเห็นว่า operator 4 ตัวหลังนี้ ถือว่าเป็นของแถม

ตัวอย่างของ Mixin ยอดนิยมของ Ruby ก็คือ Enumerable
class ที่ include Enumerable ไป จะต้อง implement method บังคับ 2 method
ก็คือ each กับ <=>
แต่หลังจาก include แล้ว ก็จะมี method ให้ใช้เพิ่มขึ้นมาอีก 22 method.
(น่าจะถือเป็นตัวอย่างที่ดีของ Think interface)

Related link from Roti

Thursday, January 17, 2008

หนังสือ The Rails Way

เห็นชื่อคนเขียนแล้วสนใจ, น้อง Roof ก็เลยแวะเอาหนังสือมาให้ผมยืมอ่านดู
เปิดอ่านบทแรกก็ประทับใจเลย เพราะมันว่าด้วย process bootstrap ของ Rails
นั่งอ่านได้อย่างเพลิดเพลิน เพราะคนเขียนรู้จริงและรู้ลึก

อย่างบทที่สองที่ว่าด้วย Controller ก็มีพูดถึงการ send_file จาก controller
คนเขียนก็พูดถึงว่า method send_file ถ้าเราไปใช้ใน mongrel
มันจะ load เนื้อหาทั้งหมดเข้ามาไว้ใน memory
ทำให้มีปัญหากับ file ขนาดใหญ่ๆได้
เขาแนะนำทางออก ด้วยการเปลี่ยนไปใช้ กลไก X-Sendfile ซึ่ง support บน Apache(module mod_xsendfile), Lighttpd
ด้วยการ ส่ง response ที่มี header เป็น

response.headers['X-Sendfile'] = path_to_file

เจ้า web server ก็จะจัดการส่ง file กลับไปให้เราเอง

หลังจากอ่านจบบท ผมก็เข้าไป check feed
ก็พบว่าใน Rails edge พึ่งมีการ submit changeset 8628
ซึ่งเป็นการปรับปรุงคำสั่ง send_file ให้มี option แบบนี้

send_file '/path/file.png', :x_sendfile => true, :type => 'image/png'

นี่ถ้าไม่อ่านมาก่อน ก็คงไม่ได้สนใจเจ้า feed นี้แน่ๆเลย

ตอนนี้วางแผนคร่าวๆไว้ว่า จะพยายามอ่านให้ได้วันละ 1 บท
จะได้รีบเอาไปคืนน้อง Roof

Related link from Roti

Tuesday, January 15, 2008

แปลงเลข Roman

หลายคนคงเคยเห็นโจทย์ประเภท

"จงเขียนโปรแกรมแปลงเลขที่รับเข้ามาให้เป็นเลข Roman"

วันนี้ผมผ่านไปเห็น haskell code ที่เขียนโดย geezusfreeek ใน reddit
แล้วก็มี Bill Mill ช่วยเขียนอธิบายการทำงาน

เห็นแล้วงามจับใจ

ถัดไปอีกวันหนึ่ง David pollak ก็เขียนด้วย scala บ้าง

Related link from Roti

Monday, January 14, 2008

Server in the house

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

วันนี้เห็นนิทานเล่มนี้ใน net แล้วอดหัวเราะไม่ได้



ลองตามไปอ่านดู Link
ในหน้า 3 จะเห็นว่าเป็นหนังสือของ Microsoft, Windows Home Server

Related link from Roti

Friday, January 11, 2008

Qi4j

Qi4j ออกเสียงว่า "chee for jay"
ความพยายามใหม่ของ Rickard Öberg (co-founder of JBoss)
ที่ตั้งชื่อว่า "Composite Oriented Programming"
โดยมีฐานต่อยอดจาก Java
Qi4j is a framework for domain centric application development, including evolved concepts from AOP, DI and DDD.


แนวคิดหลักๆคือ
1. fine-grained parts, แยกส่วน model ออกเป็นหลาย part เช่น behavior part กับ state part
แล้วเวลาใช้ ค่อยนำมาประกอบกันในลักษณะของ Composite
การแยกส่วนแบบนี้เชื่อว่า จะนำมาสู่การ reuse ที่ดียิ่งขึ้น
2. Context-base behavior, composite เดียวกัน แต่ถ้าอยู่ใน context ที่ต่างกัน
ก็จะมี behavior ที่ต่างกันได้

ลองดูเฉพาะข้อ 1 กันก่อน
ถ้าใครคุ้นกับ ruby, พออ่านข้อ 1 แล้วอาจจะนึกถึง Mixin
ลองเปรียบเทียบกับตัวอย่างใน tutorial ที่ว่าด้วย HelloWorld ของ Qi4j ดู

ปกติ HelloWorld model ถ้าเขียนแบบปกติ ก็เขียนได้ดังนี้
public class HelloWorld {
String name;

public String say() {
return "hello " + getName();
}

public void setName(String name) {
this.name = name;
}

public void getName() {
return name;
}
}

ถ้าเราแยก HelloWorld ออกเป็นส่วนๆ เราจะเห็นว่า มันประกอบด้วย
behavior part ซึ่งก็คือ method say
กับ state part ซึ่งก็คือ property name

เขียนแบบ Qi4j ก็จะต้องทำดังนี้
(ใน Qi4j เขามี concept ว่า "Classes ard dead, Long live interfaces.")

เริ่มด้วย declare model เราด้วย interface
public interface HelloWorld
extends HelloWorldBehaviour, HelloWorldState
{
}

@Mixins( HelloWorldStateMixin.class )
public interface HelloWorldState
{
Property<String> name();
}

@Mixins( HelloWorldBehaviourMixin.class )
public interface HelloWorldBehaviour
{
String say();
}

implement logic ของ behavior
public class HelloWorldBehaviourMixin
implements HelloWorldBehaviour
{
@ThisCompositeAs HelloWorldState state;

public String say()
{
return state.phrase() + " " + state.name();
}
}

implement logic ของ state
public class HelloWorldStateMixin
implements HelloWorldState
{
@PropertyField Property<String> name;

public Property<String> name()
{
return name;
}
}

สุดท้ายประกอบทั้งหมดเข้าหากัน
public interface HelloWorldComposite
extends HelloWorld, Composite
{
}


ลองเปรียบเทียบกับการเขียน Mixin ด้วย ruby ดูบ้าง
module HelloState
attr_accessor :name
end

module HelloBehavior
def say
puts "hello #{@name}"
end
end

class HelloWorld
include HelloState
include HelloBehavior
end


ตอนนี้ tutorial ในส่วน context base behavior ยังไม่ออกมา
ก็ต้องรอดูกันต่อไป ว่าจะออกมาหน้าตาเป็นอย่างไร

Related link from Roti

Thursday, January 10, 2008

ปวดหัวกับ PXE

ช่วงนี้ผมกำลัง config พวก Network boot ด้วย PXE
แล้วก็เจอปัญหาที่ว่า เครื่องรุ่นใหม่ๆ สามารถ boot ได้ตามปกติ
แต่เครื่องรุ่นเก่ากลับเจอ error ฟ้องว่า
PXE-E53 No boot filename received

ท้าวความก่อนว่า เจา PXE protocol นี่มันจะอาศัย DHCP protocol เป็นพาหนะ
เนื่องด้วย DHCP protocol เขาออกแบบไว้ดี, มันออกแบบให้มี slot options เผื่อไว้
เจ้า PXE protocol ก็เลยอาศัย options นี่แหล่ะเป็นช่องทางไว้สื่อสาร

สำนึกแรกผม เมื่อเห็นคำว่า "No boot filename received"
ก็ต้องหมายความว่า DHCP server ไม่ได้ส่ง option นี้กลับมา
แต่ถ้ามันไม่ได้ส่งกลับมาแล้ว ทำไม เครื่องรุ่นใหม่ๆ boot ได้หล่ะ
หรือว่า config DHCP server ไม่ถูกหว่า ทำให้มันส่งกลับมาไม่ถูก
ก็เลยหมกมุ่นแต่กับการแก้ configuration ของ DHCP
ซึ่งไม่ว่าจะแก้อย่างไร ก็ไม่มีความคืบหน้าใดๆให้เห็น

หลังจากพายเรือวนในอ่างมาหลายวัน
สุดท้ายก็ต้องใช้อาวุธลับ นั่นก็คือ wireshark + tcpdump เข้ามาช่วย debug
(โดยมีพี่สมภพเป็นคนช่วยแกะ packet)
ได้ความว่า

ในเครื่องใหม่ๆ protocol ของ PXE (เฉพาะครึ่งแรก ตอนที่ solve หา bootfile name กับ tftp server)
จะเป็นดังนี้


  • client broadcast จาก port 68 ไปยัง port 67 อันนี้เรียกว่า DHCP Discover
  • server broadcast กลับมา พร้อมค่า configuration ต่างๆ อันนี้เรียกว่า DHCP Offer
  • client broadcast ไปอีก โดยส่งค่า configuration ที่ได้จากขั้น 2 ไปด้วย ขั้นนี้เรียก DHCP Request
  • server broadcast ยืนยันรับทราบกลับมา ขั้นนี้เรียกว่า DHCP ACK

หลังจาก 4 step นี้แล้ว เจ้า PXE client ก็จะเข้าสู่ขบวนการ download file จาก tftp server

แต่ในเครื่องเก่าๆ protocol ของ PXE มันเป็นแบบนี้
เริ่มด้วย 4 อันแรกนี่เหมือนปกติ ทุกอย่าง แต่มันเพิ่มมาอีก 1 อันคือ


  • client ส่ง DHCP Request ไปที่ server ซำ้อีกครั้ง
    แต่สิ่งที่แตกต่างจากขั้นแรก ก็คือ
    • การส่งไม่ได้ใช้กลไก broadcast เหมือนครั้งแรก (เนื่องจากมันได้เบอร์ IP ของตัวเองมาแล้ว)
    • port ที่ request ไป กลายเป็น port 4011



เมื่อ check ที่ server ก็พบว่า ที่ port 4011 นั้น ยังไม่มี process start อยู่เลย
ความคิดแรกสุด ก็คือ เราทำ port forwarding จาก port 4011 ส่งไป 67 ดีไหม
หรือ จะติดตั้ง PXE daemon ซึ่งปกติเป็นคนรับผิดชอบเจ้า port นี้อยู่

สุดท้ายเลือกทางง่าย ก็คือลง PXE daemon ไปก่อน (เพราะทำ forward ไม่เป็น)

Related link from Roti

Friday, January 04, 2008

Singleton ?

ใครใช้ Java ก็คงรู้สึกว่า Singleton เป็นส่วนหนึ่งของชีวิต (โดยเฉพาะ Lazy-loaded)
เมื่อก่อน (นานมาแล้ว)
เขานิยมใช้ pattern แบบนี้
class MyClass {
private static MyClass instance;

private MyClass() {}

public static MyClass getInstance() {
if (instance == null) {
instance = new MyClass();
}
return instance;
}
}

ซึ่งก็ใช้ได้ ยกเว้นว่า ถ้าอยู่ในสภาพแวดล้อมแบบ concurrency แล้ว ก็มีโอกาสที่ MyClass จะถูกสร้างซ้ำซ้อนได้มากกว่า 1 ครั้ง
สมัยนั้นมีคนถกเถียงเรื่องนี้อยู่วุ่นวาย แต่ผมขี้เกียจอ่านและติดตาม
(ใครสนใจว่าถกกันอย่างไร ลองอ่านตัวอย่างอันนี้ดู The double-checked locking is broken)
ถึงจะรู้ว่ามีปัญหาเรื่อง concurrency แต่ผมก็ใช้ pattern นี้มาตลอด (โดยแอบเข้าข้างตัวเองว่า เอาน่า อย่างมากก็เปลือง resource นิดๆหน่อยๆ)
จนมาภายหลังที่มี springframework ออกมา ผมก็เลยยกหน้าที่นี้ให้ spring ไป

จนมาเมื่อวาน อ่านหนังสือเล่มหนึ่ง พบว่าเขามีวิธีการเขียนแบบใหม่ที่ดีกว่ากว่าแล้ว
เรียกว่า Initialization on demand holder idiom
public class Something
{
private Something() {}

private static class LazyHolder
{
private static final Something something = new Something();
}

public static Something getInstance()
{
return LazyHolder.something;
}
}


เป็นอันว่าเรื่องนี้ผมตกข่าวไป เพราะใน history ของ wiki เห็นเขาเขียนเรื่องนี้มาตั้งแต่ July 2006 แล้ว

Related link from Roti

Thursday, January 03, 2008

CouchDB เตรียมเข้า Apache

สืบเนื่องมาจากเรื่องเก่า ที่เคยพูดถึง CouchDB
ตอนนี้มีข่าวใหม่ว่า IBM เข้าไปเป็น sponsor
และจะนำ CouchDB เข้าไปอยู่ใน Apache Software Foundation อีกด้วย

Related link from Roti

Friday, December 28, 2007

Mail.app encoding

ช่วงนี้ที่บริษัทใช้ environment ผสมกัน ระหว่าง mac, windows, linux
ก็เลยเจอปัญหาพวก encoding กันบ่อยๆ

ตัว source code ไม่มีปัญหาแล้ว เพราะหลังๆผมบังคับให้ไปใช้ utf-8 กันหมดแล้ว

ตัวที่ทำให้ปวดหัวมากหน่อยก็คือ Mail
เนื่องจากผมใช้ Mail client เป็น Mail.app ของ apple
ทำให้เวลา forward, reply กันไปมา แล้วเกิดปัญหา encoding เพี้ยนขึ้น

ปัญหาหลักๆ ตัวหนึ่งเกิดที่ Mail.app
โดยปกติ mail ที่ส่งมา มันจะมี encoding แปะมาทั้งส่วน body และส่วน subject

Subject: =?windows-874?B?UkU6ILe0ys26wNLJ0uS3wg==?=
Content-Type: text/plain;
charset="windows-874"

แต่เวลาผม reply ต่อออกไป, เจ้า Mail.app ดัน set encoding เป็นแบบนี้แทน

Content-Type: text/plain;
charset=CP874;
delsp=yes;
format=flowed
Subject: =?UTF-8?B?RndkOiDguYDguKHguKXguYzguInguJrguLHguJrguJfguLXguYg=?=
=?UTF-8?B?4Liq4Lit4LiH?=

ตัว encoding ของ subject กับ body ที่ไม่ตรงกัน ถือเป็นจุดตั้งต้นของความเพี้ยน
(ยังไม่ตามต่อไป ว่า mail client ตัวไหนไปทำให้เพี้ยน แต่คาดว่าน่าจะเป็น outlook express)

ตอนนี้ผมก็เลยแก้ไข โดยการประพฤติตนเป็นพนักงานที่ดี โดยการ set default charset
ของผมให้เหมือนประชากรส่วนใหญ่ไปก่อน (ค่านี้ไม่มีหน้าจอ UI ให้ set ต้อง command line อย่างเดียว)

$ defaults write com.apple.mail NSPreferredMailCharset "CP874"

Related link from Roti

Thursday, December 27, 2007

กลับด้าน

ปกติเวลาสร้างตึกเราจะสร้างจากล่างขึ้นบน
ส่วนเวลารื้อถอนก็ทำกลับกัน นั่นคือค่อยๆทุบจากข้างบนลงมา (ยกเว้นกรณีใช้ระเบิด)

คำถามก็คือ แล้วตึกที่เขาทุบจากล่างขึ้นบนหล่ะมีไหม
พบคำตอบได้ที่นี่
http://www.dailymail.co.uk/pages/live/articles/news/news.html?in_article_id=472602&in_page_id=1770

Related link from Roti

Tuesday, December 25, 2007

vm เอ๋ย vm

ช่วงนี้ได้ server ใหม่มาใช้ เป็นพวก duo-core ด้วย ก็เลยได้ฤกษ์ทดลองเอา Xen มาใช้เสียที
(เดิมใช้แต่ vmware-server ซึ่งก็ถือว่า work มากแล้ว)

ลงแล้ว ก็เจอปัญหาอยู่หลายอย่างเหมือนกันเช่น
  • จะ enable vt ได้ ต้อง set config ใน bios ด้วย (default เป็น off)
    ทำให้หลงคิดว่าเป็นปัญหาที่ Xen อยู่พักใหญ่
  • กรณีที่เป็น Intel, แผ่นติดตั้งใหม่ๆ พวก ubuntu, opensuse ไม่สามารถติดตั้งบน xen ได้เลย
    เพราะมันใช้ isolinux ที่แสดง Logo เป็นรูป graphic
    ซึ่งจะทำให้ xen มัน hang และนิ่งไป
    ทางแก้ไข ก็คือต้องแตก แผ่นออกมา แล้วเข้าไปแก้ option ของ isolinux ไม่ให้แสดง Logo แบบ graphic
    อ่านเพิ่มเติมใน https://bugs.launchpad.net/ubuntu/+bug/83642
  • Windows 98 ติดตั้งบน Xen ไม่ได้
    ตายตั้งแต่ขั้น boot แผ่น setup เลย, โดยมีปัญหาตอน load driver ของ cdrom
  • ใช้ Xen คู่กับ vmware ไม่ได้

Related link from Roti

Friday, December 21, 2007

protected with_scope

feature หนึ่งใน rails ที่มีคนใช้บ่อยก็คือ with_scope
ใน rails 2.0, with_scope ได้เปลี่ยน visibility จากเดิมที่เป็น
public method ไปเป็น protected method แล้ว
นั่นหมายความว่า เราจะใช้ with_scope ได้เฉพาะใน model เท่านั้น (ระวังประโยคนี้ด้วย ใน ruby เราสามารถหลายๆอย่างที่ไม่น่าเป็นไปได้ได้)

ที่เป็นดังนี้ ก็เพราะว่ามีคนเอา with_scope ไปใช้ที่ระดับ controller กันเยอะ
แล้วก็เจอผล ที่ไม่ตรงตามที่คาดหวัง
เช่น เอาไปใช้ใน filter แบบนี้
แล้วไปติดปัญหาตอน save ที่มันไม่ยอม save ข้อมูล default จาก with_scope ให้
class ApplicationController  < AC::Base 
def scope_story_by_account
Story.with_scope(:find =>
{:conditions => {:account_id => current_account.id},
:create => {:account_id => current_account.id}) {
yield }
end
around_filter :scope_by_account
end

class StoryController < ApplicationController
def create
@story = Story.new(params[:story])
# do some stuff to @story here...
@story.save #...@story.account_id won't be set
end
end


แน่นอน การเปลี่ยนแปลงแบบนี้ ก็ต้องมีคนบ่น
บางคนก็บ่นว่า
I think it's a poor choice to limit something that people find useful just because it doesn't fit your idea of useful. Why not let the coder make these
decisions?


คำตอบที่ได้น่าสนใจ (David Heinemeier คนเขียน rails เป็นคนตอบเองเลย)
You must be new here :). Rails is opinionated software. We make things that we consider good style easy to do and bad style hard. Or rather, we make good style beautiful and bad style ugly. So even though with_scope is going protected, you can still use it in filters if you really, really want to. Just use send(:with_scope) -- that'll side-step the access control. Yes, that'll be ugly and it's intended to be.


อ่านตัวอย่างการใช้ที่เหมาะสมจากที่นี่ “WITH_SCOPE WITH SCOPE”

Related link from Roti

Wednesday, December 19, 2007

Caching in AssetTagHelper

ใน rails 2.0 มี feature อยู่ตัวหนึ่งที่ช่วย combine javascript หรือ CSS หลายๆ file ให้รวมกันเป็น file เดียว
ซึ่งจะช่วยเพิ่มความเร็วของการ load web ของเราได้
อย่างเช่น สมมติว่าเรามีการ include javascript ดังนี้
javascript_include_tag "prototype", "cart"

ปกติมันจะ render เป็นแบบนี้
<script type="text/javascript" src="/javascripts/prototype.js"></script>
<script type="text/javascript" src="/javascripts/cart.js"></script>

แต่ถ้า เราเปิด feature cache ด้วยการใส่ option แบบนี้
javascript_include_tag "prototype", "cart", :cache => "shop"

มันก็จะ render แบบนี้แทน
<script type="text/javascript" src="/javascripts/shop.js"></script>

โดยเจ้า shop.js เกิดจากการ concat prototype.js เข้ากับ cart.js
และ gzip ก่อนส่งให้ browser

เงื่อนไขสำคัญที่จะทำให้ cache ทำงาน ก็คือ ใน environment file ต้องมีการกำหนด
config.action_controller.perform_caching             = true

ซึ่งใน default environment ค่านี้จะถูกกำหนดเป็น true ใน production mode
และเป็น false ใน development mode

อ่านรายละเอียดใน

Related link from Roti

Tuesday, December 18, 2007

ศิษย์ - ครู

อ่านเรื่องนี้แล้วรู้สึกระทึกใจ

http://gotoknow.org/blog/phoenix-mirror/154417

อ่านแล้วนึกถึงเจ้าลูกชาย ว่าจะได้พบเจอครูแบบไหน

Related link from Roti

SimpleDB

SimpleDB เป็นบริการใหม่ของ Amazon
ประเด็นที่ผมสนใจก็คือ มีคนว่ากันว่า มัน implement ด้วย erlang
แต่บางคนก็บอกว่ามันมีพื้นฐานมาจาก Amazon 's Dynamo ต่างหาก
ซึ่งตัว Dynamo นี้ implement ด้วย Java

บน Erlang มีคนทำ database ทำนองเดียวกับที่ SimpleDB ทำเหมือนกัน
ชื่อว่า CounchDB ซึ่งออกมาก่อน SimpleDB ได้พักใหญ่แล้ว
ประเด็นที่น่าสนใจสำหรับ CouchDB ก็คือ
มัน implement ด้วย erlang + spidermonkey (javascript engine)
แต่ couchDB ก็มีปัญหาซึ่งยังแก้ไม่ตก
ซึ่งในมุมของ erlang ก็ถือว่าเป็นปัญหาที่น่าสนใจมาก (สำหรับคนที่ต้องการทำ fault tolerant)

Related link from Roti

Tuesday, December 11, 2007

native dependency with maven

วันนี้มีโจทย์ที่ต้อง config maven ให้เลือกใช้ dependency แบบที่ต้องขึ้นอยู่กับ platform
ต้นตอของเรื่องก็คือเจ้า swt library ที่ต้องเลือก jar ให้ถูกกับเครื่องที่จะไป run
เช่นถ้า build บน linux ก็ควรจะใช้ swt-linux-gtk-native.jar
หรือถ้าบน windows ก็ต้องเป็น swt-win32-native.jar

เริ่มต้นด้วยการ setup repository ก่อน
หน้าตาของ repository ก็ประมาณนี้

+ swt
+ swt-linux-gtk-native
+ 3.3.0
- swt-linux-gtk-native-3.3.0.jar
+ swt-win32-native
+ 3.3.0
- swt-win32-native-3.3.0.jar


จากนั้นก็ให้กำหนด profiles ใน pom.xml
สังเกตุว่าใน profile จะมี activate block ที่ใช้ข้อมูล os เป็น condition ในการ activate
<profiles>
<profile>
<id>unix</id>
<activation>
<os>
<family>unix</family>
</os>
</activation>
<properties>
<swt.os-dep>swt-linux-gtk-native</swt.os-dep>
<swt.version>3.3.0</swt.version>
</properties>
</profile>
<profile>
<id>win32</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<properties>
<swt.os-dep>swt-win32-native</swt.os-dep>
<swt.version>3.3.0</swt.version>
</properties>
</profile>
</profiles>


จากนั้นใน dependency list ของเรา ก็ให้อ้างถึง jar โดยใช้ property name แทน

<dependency>
<groupId>swt</groupId>
<artifactId>${swt.os-dep}</artifactId>
<version>${swt.version}</version>
</dependency>

Related link from Roti

Tuesday, December 04, 2007

dinosaur บุกโรงเรียนลูก

เนื่องในช่วงวันพ่อปีนี้ โรงเรียนอนุบาลของลูกผมก็ส่งคำเชิญมาว่า ให้ไปร่วมกิจกรรมในวันพ่อกัน
แต่มีข้อบังคับมาด้วยว่า คุณพ่อต้องเตรียมกิจกรรมมาร่วมกับเด็กๆด้วย
เนื่องจากช่วงนี้ลูกชายผมกำลังบ้า dinosaur, ผมก็เลยเตรียมอุปกรณ์ กะว่าจะไปชวนเด็กๆ ทำฉาก dinosaur กัน

ดูรูปแล้วกันว่าสนุกแค่ไหน

เริ่มจากการบรรยายสรุปก่อน


จากนั้นก็วาดรูป dinosaur, ใครอยากได้พันธุ์ไหน ระบุได้
แล้วก็ให้เด็กๆไประบายสี
ระหว่างนั้น ผมก็เตรียมทำฉาก


สุดท้ายก็มาจัดฉากกัน
dinosaur เยอะไปหน่อย คับฉาก


Note:
เด็ก 14 คน, วาด dinosaur ไป 14 ตัว
ยิ่งวาดยิ่งมัน นึกในใจว่ามือกำลังเริ่มเข้าที่เลย น่าจะมีเด็กเยอะกว่านี้อีกหน่อย

Related link from Roti

Wednesday, November 28, 2007

git-stash

เมื่อวาน sirn comment ถึงเจ้า Mercurial Queues extension ของ Mercurial
ตัว mq ไม่เคยใช้ แต่อ่านดูถึง feature ที่สามารถทำ stack ของ patch ได้ก็รู้สึกเคลิ้ม

วันนี้อ่านเจอคำสั่ง git-stash ที่สามารถ pending สิ่งที่ทำอยู่ แล้วย้อนกลับไปที่ clean state ก่อนหน้าได้
อันนี้แหล่ะ use-case ที่เจอบ่อย

สมมติกำลังทำ feature A อยู่ ระหว่างที่กำลังเมามันกับการ refactor
ก็ดันเกิดมี bug ร้ายแรงที่ต้องรีบแก้
ถ้าเป็น svn ง่ายสุด ก็อาจจะ checkout อีก copy
แล้วตามไปแก้ที่นู่น
แต่ถ้าเป็น git หล่ะ
# while doing something
git stash
# fix bug
git commit -a -m "xxxxx"
git stash apply
# กลับมาเมามันต่อ

Related link from Roti

Tuesday, November 27, 2007

Git อีกทีน่า

วันนี้ลองมาดูประเด็นที่ทำให้คนที่เคยใช้ cvs หรือ svn เกิดอาการเหวอเมื่อใช้ git ได้

ขั้นตอนปกติในการใช้ git
  1. สร้าง working copy ด้วยคำสั่ง git clone url
  2. แก้ไข file ที่ต้องการ
  3. สั่ง git add yourfilename เพื่อ mark ว่าต้องการ commit file นี้ในอนาคต
  4. สั่ง git commit
ดูเผินๆแล้ว จะเห็นว่าไม่น่ามีอะไร
สำหรับ user ที่ใช้ svn มาก่อน ก็จะบอกว่า
ขั้นที่ 3 มันเป็นขั้นที่เกินมาเนี่ย ใน svn ไม่เห็นต้องสั่งอะไรแบบนั้นเลย

ถ้าไม่รู้ว่าจริงๆแล้ว git ทำอะไรในชั้นที่ 3 มันก็อาจจะทำให้เกิด error ขึ้นได้
ลองจำลองเหตุการณ์ใหม่
  1. user clone project
  2. user แก้ไข file x
  3. user สั่ง git add x เพื่อบอก git ว่าต้องการที่จะ commit file นี้
  4. user กลับไป edit file x อีกรอบ
  5. user สั่ง git commit
ผลก็คือ เฉพาะการแก้ไขในขั้นที่ 2 เท่านั้น ที่ถูก commit เข้าไป
การแก้ไขในขั้นที่ 4 ไม่รวมอยู่ในนั้นด้วย
ขั้นตอนที่ถูกก็คือ
  1. user clone project
  2. user แก้ไข file x
  3. user สั่ง git add x เพื่อบอก git ว่าต้องการที่จะ commit file นี้
  4. user กลับไป edit file x อีกรอบ
  5. user สั่ง git add x อีกรอบ
  6. user สั่ง git commit
จะเห็นว่า git มีความแตกต่างกับพวก svn หรือ cvs อยู่
ตัว git เองมองสิ่งที่ track คือ content ไม่ใช่ file ทั้ง file
การที่เราสั่ง git add เป็นการสั่ง add content ที่เปลี่ยนแปลง ณ ขณะนั้น
การเปลี่ยนแปลงที่ตามหลังมา ไม่นับว่าอยู่ในกลุ่มที่จะ commit

keyword "Content" ยังนำความแตกต่างมาสู่วิธีการ commit change ด้วย
เราสามารถจัดการ content ที่ต้องการให้้ git commit ได้ในระดับ fine-grained
สมมติว่ามีการเปลี่ยนแปลงใน file ที่เราแก้ไข 5 จุด
เราสามารถเลือกได้ว่า การ commit ครั้งนี้ เราต้องการที่จะ commit เฉพาะจุดที่ 2 กับ 3 เท่านั้นนะ

ในการที่จะจัดการกับ content ที่จะ commit
เราสามารถทำได้โดยการ switch เข้าสู่ mode interactive ด้วยคำสั่ง
git add -i
ลองดูตัวอย่างการใช้งาน
สมมติว่าผมมี file x อยู่ ใน file นี้ผมมีการแก้ไข 2 จุดก็คือ
มีการ insert บรรทัดแรกสุด กับ บรรทัดท้ายสุดลงไป

เริ่มด้วยคำสั่ง git add -i
git ก็จะแสดงให้เห็นว่า file x มีการเปลี่ยนแปลงเพิ่มบรรทัดอยู่ 2 จุดนะ

pphetra@pann:~/temp/s$ git add -i
staged unstaged path
1: unchanged +2/-0 x

*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help

ลองสั่ง help ดู ก็จะได้คำอธิบายย่อๆดังนี้

*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> 8
status - show paths with changes
update - add working tree state to the staged set of changes
revert - revert staged set of changes back to the HEAD version
patch - pick hunks and update selectively
diff - view diff between HEAD and index
add untracked - add contents of untracked files to the staged set of changes

ลองเลือก menu patch
มันก็จะแสดง file ขึ้นมา ให้เราก็ระบุหมายเลข file ที่ต้องการ

*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> 5
staged unstaged path
1: unchanged +2/-0 x
Patch update> 1
diff --git a/x b/x
index abe1496..61916db 100644
--- a/x
+++ b/x
@@ -1,3 +1,5 @@
+hi bunny
hello
hi pok
hi pann
+hi pune
Stage this hunk [y/n/a/d/s/?]? ?
y - stage this hunk
n - do not stage this hunk
a - stage this and all the remaining hunks
d - do not stage this hunk nor any of the remaining hunks
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks

กรณีนี้เราต้องการ ที่จะ commit เฉพาะ row บนสุดเท่านั้น
เราก็สั่ง split
จากนั้น git จะแสดงการเปลี่ยนแปลงให้เราตัดสินใจทีละอัน

@@ -1,3 +1,5 @@
+hi bunny
hello
hi pok
hi pann
+hi pune
Stage this hunk [y/n/a/d/s/?]? s
Split into 2 hunks.
@@ -1,3 +1,4 @@
+hi bunny
hello
hi pok
hi pann
Stage this hunk [y/n/a/d/j/J/?]? y
@@ -1,3 +2,4 @@
hello
hi pok
hi pann
+hi pune
Stage this hunk [y/n/a/d/K/?]? d
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now> 7
Bye.
จะเห็นได้ว่า การจัดการ patch ของ git เร้าใจกว่า svn เยอะ
แต่ก็อาจจะนำความเวียนหัวมาสู่ผู้ที่คุ้นเคยกับโลกของ svn อย่างเดียวได้

Related link from Roti

Monday, November 26, 2007

Dojo DateWidget in Thai [Dojo 1.0]

จากปีก่อน ที่เขียนถึง Dojo DateWidget in Thai สำหรับ version 0.4
วันนี้ได้ฤกษ์ลอง version 1.0 บ้าง



ใน version 1.0, widget สำหรับ date ใช้ชื่อว่า DateTextBox
ซึ่งประกอบด้วย widget 2 ตัวประกอบกัน ก็คือ TextBox กับ _Calendar
โดยตัว _Calendar จะเป็น popup ที่ทำหน้าที่แสดงปฎิทิน
(เครื่องหมาย _ นำหน้า หมายความว่า dojo ตั้งใจให้ widget นี้เป็น internal widget)

สิ่งที่เราต้องการ ก็คือ เราต้องการให้ internal state ของ DateTextBox
ยังเก็บค่า Date เป็น Date object ปกติของ javascript อยู่
แต่ต้องการให้เฉพาะส่วนการแสดงผลเท่านั้น ที่แสดงผลเป็นปี พ.ศ.
นอกจากนั้นยังต้องการให้ calendar ที่ popup ขึ้นมาแสดงผลเป็นภาษาไทยด้วย

ใน dojo 1.0, ตัว widget ซับซ้อนขึ้น
มีการ reuse เยอะขึ้น อย่างเช่น DateTextBox นั้นมี inherited structure ดังนี้

DateTextBox -> TimeTextBox -> RangeBoundTextBox ->
MappedTextBox -> ValidationTextBox -> TextBox

จากการไล่การทำงานของ DateTextBox เราจะพบจุดตัดที่เราสามารถ override
ส่วนการแสดงผลได้ที่ method format และ parse
สามารถนำมา declare class ตัวใหม่ที่ชื่อ ThaiDateTextBox ได้ดังนี้
dojo.declare("ThaiDateTextBox", [dijit.form.DateTextBox], {

// กำหนดตัว calendar ตัวใหม่ที่แสดงผลภาษาไทย
_popupClass: "ThaiCalendar",

// value ที่รับเข้ามา มี type เป็น date object
// เมื่อได้มาแล้วก็โยนไปให้ dojo.date.locale.format จัดการ format ให้ก่อน
// แล้วค่อยนำมาตัด ปี เพื่อ + 543 หรือ 43 เข้าไปอีกที (ขึ้นอยู่กับ format)
// note: มี bug กรณี format yy ที่จะมี overflow
format: function(value, constraints) {
if(!value || value.toString() == this._invalid){ return null; }
var sidx = constraints.datePattern.indexOf('yy');
var fvalue = dojo.date.locale.format(value, constraints);
var ycnt = 2;
var adj = 43;
if (/yyyy/.exec(constraints.datePattern)) {
ycnt = 4;
adj = 543;
}
return fvalue.substr(0,sidx) + (parseInt(fvalue.substr(sidx, ycnt)) + adj);
},

// value ที่รับเข้ามา จะอยู่ในรูป datePattern ที่เรากำหนดตอน declare widget
// เมื่อได้มา ก็ทำการ - 543 เพื่อแปลงเป็น ค.ศ. ก่อนส่งให้ dojo.date.locale.parse
parse: function(value, constraints) {
var sidx = constraints.datePattern.indexOf('yy');
if (/yyyy/.exec(constraints.datePattern)) {
nyear = parseInt(value.substr(sidx, 4)) - 543;
} else {
nyear = String(parseInt(value.substr(sidx, 2)) + 2500 - 543).substr(2,2);
}
var newValue = value.substr(0, sidx) + nyear;
return dojo.date.locale.parse(newValue, constraints);
}
});


ส่วนตัว Calendar ที่ popup ขึ้นมาให้ user เลือกนั้น
ถ้าไล่ดู code ภายใน จะเห็นได้ว่า เขามีกลไก i18n ในการแสดงผลพวกชื่อเดือน ชื่อวัน
แต่เนื่องจากเจ้า dojo 1.0 มันไม่ได้ bundle locale ของไทยมาให้ด้วย
เราจึงต้องสร้างขึ้นมาเอง

ในการที่จะสร้าง locale th
เจ้า dojo ได้เตรียมกลไกไว้เรียบร้อยแล้ว
โดย script ที่จะใช้สร้าง จะอยู่ใน directory util/buildscript/cldr
ซึ่ง dojo ได้นำข้อมูล xml จาก Common Locale Data Repository
และเตรียม xlst file ซึ่งใช้สำหรับ generate เป็น javascript code ให้เราแล้ว
แค่เราเข้าไปแก้ไข build.xml เพิ่ม "th-TH" เข้าไปในรายการที่ต้องการให้ generate

<!-- Arbitrary defaults. locales and currencies properties can be altered or eliminated to build the entire set -->
<property name="locales" value="en-us,th-th"/>


ประเด็นปัญหาถัดไปก็คือตัวปี พ.ศ.
ถึงแม้เราจะเลือกใช้ locale th-TH แล้วก็ตาม
มันก็ยังมีปัญหาว่า ปี ยังได้เป็นปี ค.ศ. อยู่ดี
ตรงส่วนนี้ เราต้องแอบเข้าไป override method ใน Calendar widget
ในใน dojo 0.4, จุดตัดสำหรับเรื่องนี้ค่อนข้างง่าย เพราะคนเขียนเขาแบ่ง method ออกเป็น method ย่อยๆ เพื่อให้อ่านง่าย
แต่ใน dojo 1.0 คนเขียน เขาเขียนส่วน render ยาวเป็นพรืด ทำให้เราเลือก override ยากหน่อย

สำหรับกรณีนี้ เราเลือก override method "_setText"
ซึ่งเป็นทางเลือกที่ไม่ค่อยดีนัก เพราะเป็นการ override แบบที่แอบไปใช้กลไกภายในของมัน
เพราะมันมีผลว่า ถ้ามันมีการ upgrade internal structure เมื่อไร
class ของเราก็จะมีโอกาส broken ได้โดยง่าย
dojo.declare("ThaiCalendar", [dijit._Calendar], {
_setText: function(node, text) {
if (/YearLabelNode/.exec(node.getAttribute("dojoattachpoint"))) {
arguments[1] = parseInt(text) + 543;
}
return this.inherited('_setText', arguments);
}
});


เวลานำไปใช้ ก็ใช้แบบนี้
<script type="text/javascript" src="./js/dojo/dojo.js"
djConfig="isDebug: true, parseOnLoad: true, locale: 'th'"></script>

...

<span id="d1" dojoType="ThaiDateTextBox"
constraints="{datePattern:'dd/MM/yy'}"/>

Related link from Roti

Thursday, November 22, 2007

ทำความรู้จัก LVS

งานของผมนอกเหนือจากพัฒนา application ที่เป็น web แล้ว
ก็ยังมีงานเก่าๆที่เป็นพวกจอเขียวๆ (พวก terminal) อยู่ด้วย

ช่วงนี้มีโจทย์ที่ต้อง solve ก็คือ ต้องทำให้ telnel service นั้นมีคุณสมบัติ HA (High Availability)
ตัวเครื่องมือที่เลือกใช้ก็คือ LVS (Linux Virtual Service)
ซึ่ง support service ที่ run อยู่บน TCP หรือ UDP
ตัว LVS จัดได้ว่าเป็น Layer 4 Switching

คำศัพท์เบื้องต้น

  • Director คือเครื่องที่ทำหน้าที่รับ packet จาก client และ forward ต่อให้ Real Server

  • Real Server คือเครื่องที่ทำหน้าที่ให้บริการจริง

  • Virtual IP Address (VIP) คือ IP ที่ client ใช้ติดต่อ service

  • Real IP Address (RIP) คือ IP ของ Real Server




รูปแบบการ config LVS แบ่งออกเป็น 3 ประเภทตามลักษณะการ forward packet คือ
  1. NAT (Network Address Translation)
    packet ที่ส่งมาจะถูกแปลงเบอร์ IP address แล้วส่งต่อให้ real server
    ส่วน packet ที่ real server ตอบ ก็จะต้องส่งผ่าน Director เพื่อแปลง IP Address กลับเป็นตัวเดิม
  2. DR (Direct Routing)
    packet ที่ส่งมาจะไม่ถูกแปลง แต่จะถูก forward ไปให้ real server เลย
    เมื่อ real server ตอบ, packet ก็จะส่งกลับไปที่ client โดยตรง
    Note: เพื่อให้ real server ยอมรับ packet ที่ forward มา (ซึ่งมี IP Address เป็น VIP)
    ก็ต้องมีสร้าง hidden dummy interface)
  3. IP-IP Encapsulation (Tunnelling)
    คล้ายๆ direct routing แต่ packet ที่ถูก forward จะถูกห่อก่อนจะส่งไปให้ real server
    เพื่อที่จะได้ส่งข้ามวง network ได้


concept ที่สำคัญอันหนึ่งของ LVS ก็คือ Scheduling
หรือการจัดสรร request แจกให้กับ real server
ซึ่งมี algorithm ให้เลือกเยอะเลย เช่น

  • Least-Connection (lc): Allocate connections to the real-server with the least number of connections.

  • Weighted Least-Connection (wlc):

  • Weighted version of Least-Connection.

  • Round-Robin (rr): Place the real-servers in a circular list and allocate connections to each real-server in turn.

  • Weighted Round-Robin (wrr): Weighted version of rount-robin.

  • Locality-Bassed Least-Connection (lblc): Try to assign connections addressed to the same IP address to the same real-server. This is frequently used in conjunction with transparent http proxy services.

  • Locality-Based Least-Connection with Replication (lblcr): Variation of Locality-Bassed Least-Connection that allows a pool of servers for a given destination IP address to be maintained in sutiations of high load.

  • Destination-Hashing (dh): Use a static hash of the destination IP address to allocate connections.

  • Source-Hashing (sh): Similar to Destination-Hashing, but the source IP address is hashed.

  • Shortest Expected Delay (sed): Allocate connections to the server that will service the request with the shortest expected delay.

  • Never Queue (nq): Allocate a connections to a idle real-servers if there are any, else use the Shortest Expected Delay altgorithm.


ที่ผมชอบก็คือ เราสามารถ config น้ำหนัก(weight) ให้กับแต่ละ server ได้
ทำให้เรา mix real server ระหว่างเครื่อง performance ดี กับเครื่องปกติได้

การใช้ LVS อย่างเดียว ไม่สามารถทำ HA ได้
เนื่องจากเราต้องเตรียมเครื่อง Director ไว้มากกว่า 1 เครื่อง
ทำให้เราต้องใช้ Heartbeat เข้ามาช่วย config ว่า
ถ้าเครื่อง director เครื่องหลักตาย เครื่องสำรองต้องเข้ามา take over งานไปทำ

นอกจากนั้นยังมีประเด็นที่ Director ยังต้องสามารถ monitor เครื่อง Real server ด้วย
ถ้า real server ตายไป จะได้จัดการปรับปรุง route table ของตัวเอง ไม่ให้แจกงานไปที่ server ตัวนั้นอีก
กรณีนี้ผมเลือกใช้ Ldirectord

เนื่องจาก service ที่ผมจะ implement คือ telnet service
ที่มีธรรมชาติที่เปิด connection ค้างไว้ตลอด
กรณีที่ Director down ไป จะทำให้ session ของ user หลุดหมด
เพื่อแก้ปัญหานี้ ก็ต้องเลือกใช้ Connection Synchronisation
โดยการระบุให้เครื่องหลักเป็น master และเครื่องสำรองเป็น backup

default synchronization model ของ LVS เป็นแบบ master-slave ซึ่งจะมีปัญหาตามมาว่า
ถ้าเครื่องหลักมัน up ขึ้นมาอีกครั้ง การส่งข้อมูล routing table กลับจะทำอย่างไร
สำหรับกรณีนี้มีคน implement model การ synchronization แบบ peer-to-peer ขึ้นมา
ซึ่งผมเห็นแต่ในเอกสาร แต่ยังหา source ไม่เจอ

Related link from Roti

Sunday, November 18, 2007

ความผิดพลาด

ผมชอบที่อาจารย์สกล เขียนไว้ในนี้ Link

ความผิดพลาดนั้นเป็นของควบคู่กับงาน แต่ความผิดพลาดของหมอกับของครูนั้น มีความรุนแรงเหมือนกันอยู่อย่างนึง ของหมอนั้น ความผิดพลาดมักจะไปลงเอยหลายที่ หน้าหนังสือพิมพ์ website ปาฐกถา บรรยาย small group, etc และเป็นเรื่องของความเป็น ความตาย หรือ พิการ ส่วนความผิดพลาดของครูนั้น อาจจะหมายถึงชาติกำเนิดและการพัฒนาจิตที่บิดเบี้ยว เสียรูปร่างอย่างแรง และกรอบการเติบโตอันคับแคบ อับจนปัญญา ไร้ความคิดคะนึงหา ติดกับดักวิญญาณ การใช้ชีวิตที่ไม่เต็มสมศักยภาพของตนเอง

Related link from Roti

Thursday, November 15, 2007

ย้อนทบทวน git

ในงาน codefest เมื่อสองอาทิตย์ก่อน ผมทดลองนำ git มาใช้
ซึ่งผลของเวลาที่จำกัด + การที่ไม่เคยใช้มันมาก่อน ก็ทำให้มีประสบการณ์ที่ไม่ค่อยดีกับมันนัก

เริ่มแรกสุดลองดู graph ของการ merge ที่เกิดขึ้นก่อน



ดูก็สวยดีนะ แต่จริงๆแล้วมันมั่วมาก
เนื่องจากมี คน develop อยู่ 4 คน
ประเด็นแรกก็คือ มันควรจะตั้งให้คนใดคนหนึ่งเป็น ศูนย์รวม
แล้วก็ให้แต่ละคนที่เหลือ push กับ pull ผ่านทางจุดที่เป็นศูนย์รวมนั้น
แต่ปรากฎว่าเวลา เรา start git-daemon
default mode ของมันก็คือ pull ไปได้อย่างเดียว ไม่รับ
การ push กลับ
เนื่องจากช่วงนั้นผม concentrate กับ erlang อย่างเดียว
เลยไม่มีสมาธิมาลองหาดูว่า ต้องใส่ flag อะไร เพื่อให้มันสามารถรับการ push ได้
ตอนนั้นเลยแก้ปัญหาด้วยการใช้ pull อย่างเดียว
ใครทำอะไรเสร็จ ก็ให้ start server จากนั้นก็ให้ทีเหลือ pull ออกมาจากคนนั้น
มารู้ทีหลังว่า ถ้าต้องการ start daemon ให้รับ push ได้ด้วยนั้น
ต้องไส่ flag ให้มัน enable service "receive-pack" ด้วย

git-daemon --reuseaddr --export-all --base-path=. --enable=receive-pack .


ประเด็นถัดไป ก็คือ
ปกติในการเก็บ version control เราจะ ignore file พวกที่เป็นผลลัพท์จากการ compile
ใน git เราสามารถระบุ file ที่ ignore ได้ใน file .git/info/exclude
แต่ปัญหาก็คือ ตอนที่มีคน clone code ออกจาก repository เรา
เจ้า file exclude มันไม่ได้ไปด้วย
ผลก็คือ น้องๆที่เอาไปพัฒนาต่อ commit file พวกนี้เข้าใน repository กันทุกคน
พอถึงเวลา pull กลับมา มันก็จะเกิด error ฟ้องว่า merge ไม่ได้
วันนี้มานั่งอ่านคู่มือไล่หาว่าเราควรกำหนด exclude file อย่างไรดี
กลายเป็นว่า เวลาเรา clone repository
file ที่อยู่ใต้ .git มันจะเอามาจาก /usr/share/git-core/templates

Related link from Roti

Friday, November 09, 2007

Ruby Business Commons

เมื่อวานไปนังฟัง Mr.Eihiro Saishu present หัวข้อเรื่อง Ruby Business Commons
ฟังหัวข้อตอนแรก นึกยังไงก็นึกไม่ออกว่ามันเป็นเรื่องเกี่ยวกับอะไร, ค้นใน net ก็พบว่ามี project อยู่ใน rubyforge
ตามไปดูก็ไม่พบข้อมูลเพิ่มเติม เห็นแต่ว่ามี code กองอยู่จำนวนหนึ่ง

ได้ไปฟังแล้วก็ประทับใจมาก เหมือนได้ฟังพวกคอเดียวกันพูด
present ของ Eihiro ทำออกมาได้แตกต่างจากคนอื่นในงานดี
ออกมาในแนวของ ใช้คำน้อย, สื่อสารด้วย keyword ไม่ไช่ bullet

Ruby Business Commons คือ Community ของคนที่มี passion ใน ruby
รวมกลุ่มกัน เรียนรู้, พัฒนา และที่สำคัญก็คือ สังสรรค์
แล้วก็มีกิจกรรม knock (ร่วมกลุ่มกัน implement project)
ตัวอย่าง knock ครั้งที่ 1 ของเขาที่เมือง Tenjin น่าสนใจมาก
ลองอ่านเพิ่มเติมใน slide
ส่วน web site ของตัว Community อยู่ที่นี่ Link
แต่เสียใจด้วยมันเป็นภาษาญี่ปุ่น (โชคดีที่บริษัทฯผมมีคนอ่านญี่ปุ่นได้)

อ่านกิจกรรม knock ของเชาแล้ว เริ่มฝันเห็นแนวทางรำไรของ Bangkok BarCamp แล้ว

อานิสงค์ของการไปฟังครั้งนี้ ก็เลยได้พบคนใช้ ruby ในไทยเพิ่มอีกหลายคน
เช่น น้องฝน(ที่เขียน wiki เรื่อง rails) และคุณชุมพล ครุฑแก้ว จาก nectec
แล้วก็น้องพีรพงษ์ จากบริษัท Thai Software Engineering
ที่กำลังใช้ rails พัฒนา product ให้กับบริษัทญี่ปุ่น(กล้องนิคอน)

Related link from Roti

Thursday, November 08, 2007

จบ Codefest ไปอีกครั้ง

คราวนี้โชคไม่ดีตรง กอบ เพื่อนร่วมทีมผม ที่รับผิดชอบงานด้าน
server side erlang เกิดไม่สบาย
ผมเลยต้องทำแทน
ด้วยความที่ไม่ได้เตรียมตัวเท่าที่ควร (เตรียมอยู่ 2 วัน)
อาการ panic ก็เลยเกิดขึ้น
ช่วงเช้าถึงบ่าย เป็นเวลาทีื่ชุลมุนที่สุด
เนื่องจากอยู่ในช่วง setup ปรับตัวกัน
ต้องแบ่งสมาธิไปทำหลายอย่าง ทำให้ไม่มีสมาธิที่จะทำโปรแกรม

ทืมผมได้กว้านเอาน้องๆแถวนั้นมาร่วมทืมอีก 3 คนคือ
น้องป้อ (iporsut.wordpress.com) จากสุรนารี
น้องซี (kobkrit.blogspot.com) กับน้อง boy จากธรรมศาสตร์
3 คนนั้นก็เลยได้ลิ้มรส erlang เป็นครั้งแรก
น้องป้อได้เปรียบหน่อยตรงที่คุ้นชินกับ haskell มาแล้ว

ตัว version control ที่ใช้ก็คือ Git
ซึ่งสร้างความปวดหัวเวียนเกล้าได้มาก เวลาที่มันเกิด exception case ขึ้นมา
(เวลาเราอ่าน tutorial เรามักจะทำไปตาม flow ที่รื่นไหล)
ร่ำๆจะเปลี่ยนไปใช้ svn แทน

การเขียน erlang คราวนี้ทำให้รู้จุดอ่อนอย่างหนึ่ง
(จุดอ่อนของคนเขียน ไม่ใช่ของภาษา)
เนื่องจาก erlang มี characteristic อย่างหนึ่งก็คือเรื่อง fail-fast
พอมี process หนึ่งตาย โปรเซสที่ link กันอยู่ ก็จะตายด้วย
ด้วยความที่เราทดสอบโปรแกรม จาก shell command เป็นหลัก
พอเราทำอะไรผิดนิดผิดหน่อย โปรเซส shell ก็จะตาย
ส่งผลให้ process ที่ start ด้วย generic behavior ทั้งหลาย
(พวกนี้จะ link กับ process แม่ที่ start มัน)
ตายตามเป็นแถว
ดังนั้นงาน routine ส่วนใหญ่ใน codefest ก็คือ restart process.
ตอนนั้นก็ได้แต่สงสัยว่า เอ๊ะเราไม่ได้ link ตัวนี้กับตัวนั้น
แต่ทำไมพอตัวนี้ตาย ตัวนั้นตายด้วยหว่า
มาร้องอ๋ออีกที ตอนสายๆของอีกวันหนึ่ง

จุดอ่อนการเขียน erlang อีกเรื่องก็คือการใช้ io:format
ที่เรามักใช้ในการ print output ออกมาดูที่ console ว่า โปรแกรมทำงานถูกต้องตามที่ต้องการไหม
ปรากฎว่า io:format มันมักจะรบกวนการทำงานของ process
ส่งผลให้ process มันมีพฤติกรรมแปลกๆไป
อย่างเช่น socket ที่เปิดอยู่ ถูกปิดไป, โดย process ที่ control ไม่ได้ตายไปด้วย
bug ตัวนี้ผมใช้เวลาหาอยู่ตั้งแต่ช่วงหกทุ่มถึง 7 โมงเช้า
ร่ำๆจะยอมแพ้ ไม่จบงาน ก็หลายที
แต่สุดท้ายมาปิ๊งว่าเป็นเรื่องนี้ ตอนที่ไปอาบน้ำ

งาน codefest คราวนี้มีประเด็นที่น่าสนใจอยู่เรื่องก็คือ
lead ในงานคราวนี้คือทืมจากญี่ปุ่น, ซึ่งเขามีแนวทางในการ lead project
ที่น่าสนใจมาก เช่นใช้ wiki เป็นตัวสื่อสารหลัก
การจัด schedule เวลา ฯลฯ
เสียดายมากที่ผมต้องทุ่มสมาธิไปที่ erlang อย่างเดียว
ก็เลยไม่ได้เข้าไปสังเกตและเรียนรู้วิธีการของเขา

ส่วนงานอีกเรื่องที่เกี่ยวกับ MapServer
อันนี้ผมเตรียมมาก่อนแล้ว น้องๆก็เลยทำกันเองได้ ไม่ต้องเข้าไปช่วย
ซึ่งผมคิดว่าคุ้มมากที่พาน้องๆที่บริษัทฯมา
เพราะ
1. การโปรแกรมในเวลาระยะสั้น ต้องการการสื่อสารที่ชัดเจน
ต้องการความเข้าใจใน tool, framwork เพื่อใช้ในการวินิจฉัยปัญหา
ปัญหาที่น้องๆเจอ น่าจะทำให้เขารู้ว่า จุดอ่อนของเขามันมีอยู่ที่ไหนบ้าง
เช่น น้องอั๋นหาวิธีการ response ajax request แบบไม่ได้ใช้ template render
อยู่หลายชั่วโมง, ซึ่งถ้าเขาเข้าใจ concept ของ framework ชัดเจน
เวลาในการ solve ก็จะเหลือไม่กี่นาทีแทน
2. การ present ผลงาน ทำให้เรารู้จักเตรียมตัว, การซักซ้อม,
การคัดเลือกเนื้อหาที่จะนำเสนอ
งานนี้ผมปล่อยให้น้องๆนำเสนอกันเอง
ซึ่งก็เกิดความผิดผลาดกันถ้วนหน้า
เช่นคุมเวลาไม่ได้, เนื้อหาไม่น่าสนใจ, program ไม่ work อย่างที่อยากจะแสดงให้ดู

Related link from Roti

Friday, November 02, 2007

XMLSocket(Flex) กับ Erlang

protocol ของ XMLSocket กำหนดไว้ว่า ข้อมูลที่ส่งแต่ละชุดจะปิดท้ายด้วย Zero Byte
นี่คือ code ง่ายๆของ Erlang ที่เปิดรับข้อมูลที่ส่งจาก XMLSocket
%% echo server แบบง่ายๆ
start() ->
{ok, Listen} = gen_tcp:listen(4114, [list,
{reuseaddr, true},
{active, true}]),
{ok, Socket} = gen_tcp:accept(Listen),
gen_tcp:close(Listen),
loop(Socket).

loop(Socket) ->
receive
{tcp, Socket, Data} ->
%% ก่อน parse ก็ให้ตัด byte สุดท้ายที่เป็น zero byte ออกก่อน
Txt = xmlutil:parse(strip(Data, right, $\0)),
%% ตอนส่งกลับก็ให้ต่อท้ายด้วย zero byte กลับไปด้วย
gen_tcp:send(Socket, concat(xmlutil:encode(concat(Txt, " pok")), [0])),
loop(Socket);
{tcp_closed, Socket} ->
io:format("Socket closed~n")
end.


ตัว XMLSocket ถึงจะตั้งชื่อให้มี XML นำหน้า แต่จริงๆแล้วมันก็เหมือน XMLHttpRequest ของ javascript
ที่ใช้ส่งข้อมูลอะไรก็ได้ ตามแต่ใจเรา
อย่างที่ผมเอามาใช้ แทนที่จะส่งข้อมูลมาให้ erlang ในรูปแบบ XML (ซึ่งเราขี้เกียจ parse)
ก็ให้ส่งมาเป็น erlang tuple เลย จะได้เอาไป execute ต่อได้ง่ายๆ

ใน erlang, มันมีวิธี eval String ง่ายๆดังนี้
eval(S,Environ) ->
{ok,Scanned,_} = erl_scan:string(S),
{ok,Parsed} = erl_parse:parse_exprs(Scanned),
erl_eval:exprs(Parsed,Environ).

ลอง run ดู
2> simple_server:eval("X=1+2.",[]).
{value,3,[{'X',3}]}
3> simple_server:eval("{plus,1,2}.",[]).
{value,{plus,1,2},[]}


เวลาเราเอาไปใช้กับ XMLSocket ก็จะเขียนประมาณนี้
loop(Socket) ->
receive
{tcp, Socket, Data} ->
Str = strip(Data,right,$\0),
{value, Cmd, _} = eval(Str,[]),
Result = execute(Cmd),
gen_tcp:send(Socket, concat(Result, [0])),
loop(Socket);
{tcp_closed, Socket} ->
io:format("Socket closed~n")
end.

execute({hello}) -> "world";
execute({who}) -> "pok";
execute({plus, X, Y}) -> integer_to_list(X+Y).


Update:
กรณีถ้าต้องการ parse จาก String เป็น Term
ใช้ code แค่นี้ก็พอ
eval(S) ->
{ok,Scanned,_} = erl_scan:string(S),
{ok,Term} = erl_parse:parse_term(Scanned),
Term.

Related link from Roti

Thursday, November 01, 2007

เตรียมการ codefest

เหลืออีก 4 วันก็ถึง codefest แล้ว
ช่วงนี้ก็เลยวุ่นอยู่กับการเตรียมการ
เนื่องจากหัวข้อที่ผมเลือกทำ codefest,
ผมเลือกจากสิ่งที่ไม่รู้หรือไม่เคยทำ
ตอนนี้ก็เลยสนุกกับการบุกเบิกในสิ่งที่ไม่รู้

โปรเจคแรก เป็นโปรเจคที่ present ข้อมูลผ่าน Map
ซึ่งต้องยุ่งกับ MapServer



ความยากจะอยู่ที่ jargon หรือ Term ต่างๆที่เป็นเรื่องทางภูมิศาสตร์
เช่นถ้าใช้ wms (web service ของพวก GIS)
ค่า SRS (Spatial Referential System) ของไทยต้องกำหนดเป็นค่าอะไร?
ความยุ่งที่สอง ก็คือ ตอน present map ด้วย javascript
ผมเลือกใช้ Ka-Map ซึ่งมี feature ทำนอง Google Map
แต่ Ka-Map มันใช้ server-side เป็น php
ซึ่งผมต้องการเปลี่ยนเป็น Rails แทน
โชคดีที่มี project Ruby In Space เขาใช้ rails เป็น server-side เช่นกัน
ก็เลยไปเอา code ของ project นั้นมาใช้
แต่ก็ไม่วายโชคร้าย ตรง code base มันเก่าแล้ว
มันก็เลยมี error จากการที่ api ของ mapserver มันเปลี่ยน signature
แต่ก็แก้ได้ไม่ยาก
ไปยากตรงงมว่ามันใช้อย่างไร
ส่วนของ embeded code, ผมทำแต่ส่วน high level ก็เลยไม่มีปัญหามากนัก
เจอ bug ตรง firmware หน่อยหนึ่ง

โปรเจคที่สองเป็นโปรเจคที่ทำด้วย erlang + flex
ความยากอยู่ที่ นานๆจะใช้มันที
มันก็เลยฝึดๆ
กว่าจะทำอะไรได้ที ต้องเปิดคู่มือวุ่นวาย

แต่ที่ต้องยกให้เป็นพระเอกของงานนี้ก็คือ
vmware server
เพราะแค่เตรียมสภาพแวดล้อมเป็น vertual machine
แล้วแจกจ่ายให้ทืม ก็ช่วยลดแรงเสียดทานไปได้เยอะแล้ว
(แรงเสียดทานของ version conflict, library ไม่ครบ, config ไม่ถูก, Windows vs Linux)

Related link from Roti

Friday, October 26, 2007

cherry-picking

ช่วงนี้กระแส git กำลังมาแรง
เวลาอ่าน feed ก็จะได้เห็นเรื่องเกี่ยวกับ dvcs เยอะหน่อย
อย่างวันนี้เจอศัพท์ว่า “cherry-picking”

cherry-picking เป็น feature หนึ่งของ darcs
ความหมายก็คือ เวลาเรา push(หรือ pull) patch ไปอีก repository หนึ่ง
เจ้าโปรแกรม darcs มันจะค่อยๆนำเสนอ patch ทั้งหมดที่เกิดขึ้นใน local repository ของเรา
จากนั้นก็ให้เราเลือกตัดสินใจว่า จะ push patch นั้นหรือไม่
@pann[~/projects/javascript/test]$ darcs push ../dojo-grid/

Fri Oct 26 11:07:32 ICT 2007 pphetraATgmail.com
* remove unused css
Shall I record this patch? (1/?) [ynWsfqadjkc], or ? for help: ?
How to use record...
y: record this patch
n: don't record it
w: wait and decide later, defaulting to no

s: don't record the rest of the changes to this file
f: record the rest of the changes to this file

d: record selected patches, skipping all the remaining patches
a: record all the remaining patches
q: cancel record

j: skip to next patch
k: back up to previous patch
c: calculate number of patches

Shall I push this patch? (1/1) [ynWvpxqadjk], or ? for help: y
Finished applying...


เกร็ด: เวลาศึกษา darcs แล้วจะงงนิดหน่อย
เพราะเรามักจะคุ้นกับ concept "versions"
เช่น checkout revision 3172, ทำ tag release 0.1
แต่ใน darcs, concept ของมันจะวางอยู่บนคำว่า "patchs"
คนเขียน darcs ก็คือ David Roundy ซึ่งเป็น physics professor ที่ Berkeley
เขาเขียนโดยอิงกับ “theory of patches” (พึ่งเคยได้ยินเหมือนกัน)

ข้อดีอย่างหนึ่งของการวาง concept อยู่บน patch
ก็คือ มันบังคับให้ developer ต้องเขียนคำอธิบาย patch ดีๆ
ไม่เหมือนกับพวก commit message ของ cvs, svn ที่ developer เขียนส่งเดชอย่างไรก็ได้
เพราะถ้าคำอธิบายไม่ดีแล้ว รับรองว่าการ sync ระหว่างกัน
มันจะมั่วและสับสนมาก

Note: git ก็มี cherry-picking เหมือนกัน
แต่มันใช้ concept คนละอย่างกับ darcs
(ซึ่งส่งผลให้แนวการจัดการแตกต่างกัน)
อ่านเพิ่มเติมเรื่องนี้ Link

อืมม์ งาน codefest คราวนี้จะใช้ git หรือว่า darcs หรือ hg ดีหว่า

Related link from Roti