Friday, November 04, 2005

Higher Order Messaging

อ่านเจอเรื่อง Higher Order Messaging ของ Mistaeks I hav Made
ก็เลยมาลองทำดูบ้าง เพื่อจะได้ซาบซื้งยิ่งขึ้น

ขั้นแรก ก็ต้องรู้ก่อนว่า Higer Order Message มีนิยามว่าอะไร
ถ้าตามไปดูใน paper ที่เขาอ้าง ก็จะพบประโยคนี้
Higher Order Messages allow user-defined message dispatch mechanism to be expressed using an optimally compact syntax that is a natural extension of plain messaging and also have a simple conceptual model.

คนอื่นเป็นไงไม่รู้ แต่ผมอ่านแล้ว ก็มึน
ดู definition อันนี้บ้าง
A higher order message is a message that takes another message as an "argument". It defines how that message is forwarded on to one or more objects and how the responses are collated and returned to the sender. They fit well with collections; a single higher order message can perform a query or update of all the objects in a collection.

ฟังดูง่ายขึ้นอีกนิด
ประมาณว่าเกี่ยวกับ การ update หรือ query Collections
โดยใช้กลไก forward message

ลองดูตัวอย่าง จะเห็นภาพกว่า
สมมติว่าเรามี Array ของ Class Person
ซึ่งภายในประกอบด้วย object จำนวนหนึ่ง
แล้วเราต้องการค้นหาชื่อเฉพาะบุคคลที่ยังเป็น Toddler (เด็กหัดเดิน)
(ตัวอย่างนี้ ได้รับแรงบันดาลใจจากลูกชาย)
ถ้าเขียนโปรแกรมแบบตรงเผงเลย ก็จะเขียนประมาณนี้
names = Array.new
for person in persons
if person.toddler? then
names << person.name
end
end
puts names


บรรดากูรูทั้งหลายเห็น code นี้แล้วบอกว่า ยาวไป
เห็นแล้วไม่เกิดแรงบันดาลใจ
ก็เลยไปทำการประดิษฐ์ block เข้ามาช่วย
code ข้างบนก็เลยกลายเป็น
puts persons.select { |p| p.toddler? }.collect {|p|  p.name} 


กูรูคนถัดไปเห็นแล้ว ก็บอกว่า block มันเกะกะ
อยากได้อะไรที่มันสร้างแรงบันดาลใจได้มากกว่านี้
ก็เลยเกิด Higher Order Message ขึ้น
ซึ่ง code ที่ได้จะเป็นดังนี้
puts persons.where.toddler?.extract.name


ตอนนี้ก็เลยเป็นหน้าที่ของโปรแกรมเมอร์อย่างเรา
ที่จะต้องเรียนรู้แล้วว่า HOM นั้น implement อย่างไร
จากตัวอย่างนี้ ผม implement ดังนี้ (จริงๆแล้ว ก็ลอกๆเขามานั่นแหล่ะ)

เริ่มด้วยสร้าง base class ก่อน
class HigherOrderMessage
def HigherOrderMessage.is_vital(method)
return method =~ /__(.+)__|method_missing/
end

for method in instance_methods
undef_method(method) unless is_vital(method)
end

def initialize(handler)
@handler = handler
end
end


จริงๆแล้ว class นี้จะเขียนแค่นี้ก็ได้
class HigherOrderMessage
def initialize(handler)
@handler = handler
end
end

ที่เขาใส่เพิ่มเข้ามา คงไว้ playsafe,
for loop ที่เขาใส่เข้ามาจะทำหน้าที่ block ไม่ให้ object ภายนอก
มองเห็น methods ของ HigherOrderMessage
ยกเว้นแต่ method ที่ชื่อ __send__ กับ __id__
ใครไม่รู้จัก __send__ ลองดูตัวอย่างนี้
[1,2].__send__('length') #=> 2
[1,2].length #=> 2

2 ประโยคข้างบนนั้นเท่าเทียมกัน

กลับมาดูกันต่อว่า where นั้น implement อย่างไร
class Where < HigherOrderMessage
def method_missing(id, *args)
return @handler.select {|o| o.__send__(id, *args)}
end
end

ก่อนอธิบายการทำงาน ต้องดูการนำไปใช้ก่อน
เนื่องจากเราต้องการให้ทำ persons.where ได้
แสดงว่า Array class ต้องมี method ที่ชื่อ where อยู่
ดังนันเราก็เลย declare mixin ขึ้นที่ Array class
class Array 

def where
return Where.new(self)
end

end


ซึ่งก็ใช้ได้ในตัวอย่างเรา
แต่ถ้าต้องการ generic กว่านั้นก็ควรไป declare ใน module Enumerable แทน
module นี้เป็นแหล่งรวม code ที่เกี่ยวกับ iterate collection
(ซึ่ง Array จะ include Enuerable อีกที)
module Enumerable 

def where
return Where.new(self)
end

end


การทำงาน ก็คือถ้ามีการเรียกใช้ where เมื่อไรก็ตาม ก็จะมีการ return Where object
กลับไป โดยภายใน where object จะมี handler ซึ่งเก็บ array object ไว้
และเมื่อมีการเรียกใช้ method อะไรก็ตาม (ดักโดยใช้กลไก method missing ของ ruby)
ก็จะทำการ loop collection นั้น
และ forward ชื่อ method ที่เข้ามาให้กับ object ใน collections นั้นอีกที
ในกรณีของเรา method toddler? ก็จะถูกใช้เป็นตัว screen ใน
select method

ส่วน extract ก็เขียนง่ายๆโดยใช้ collect ช่วย
class Extract < HigherOrderMessage
def method_missing(id, *args)
return @handler.collect {|o| o.__send__(id, *args)}
end
end


หลังจากลอง implement ดู แล้วย้อนกลับไปอ่าน definition อีกที
ก็พบว่า เริ่มซืมเข้าหัวมากขึ้นแล้ว

Related link from Roti

No comments: