Planet TLWG
Vee: The worst mistake that I made in Common Lisp programming
My programs have defects because some functions destroy or mutate shared data. I avoid mutating shared data in Common Lisp; for example, I use CONS. However, I made a mistake by using SORT, which wasn't aware that SORT is destructive. Sometimes I still forget that SORT mutates its input data. The function sort-snode below sorts a part of a tree.
Since it is based on SORT, it changes the input data, which is snode. After I found the output tree didn't look what I expected, I took days to see where my mistake was.
My workaround is running COPY-LIST before SORT, as shown below.
In brief, if you are new to Common Lisp, please beware of destructive operations, which is not limited to SORT. It can be NCONC.
Thep: From C++ to Rust So Far
บันทึกเมื่อเรียน Rust ได้ครึ่งทาง
ผมเคยเรียน Rust ไปแล้วรอบหนึ่ง ตอนนั้นเรียนจากเว็บไหนก็จำไม่ได้ เขียนบนเว็บ คอมไพล์บนเว็บ ไม่เหลือร่องรอยในเครื่องให้ทบทวน แถมเนื้อหาก็ไม่ได้ลงลึกอะไร เรียนเหมือนไม่ได้เรียน ความมึนไม่ปรากฏ และไม่นานก็ลืม รอบนี้จึงขอเรียนแบบจริงจังเพื่อหวังให้นำไปใช้ได้จริง หลังจากที่ได้เห็นการใช้งานที่หลากหลายของภาษาในที่ต่างๆ จนดูเหมือนพยายามจะแทนที่ C/C++ ให้ได้
ครั้งนี้ผมเลือกที่จะเรียนแบบมี quiz จากแหล่งซึ่งมีเนื้อหาเดียวกับหนังสือ The Rust Programming Language (the book) แต่แทรก quiz เป็นระยะ
ซึ่งผมขอวิจารณ์ว่าเนื้อหายังอธิบายประเด็นสำคัญไม่ละเอียดเท่าที่ควร บางเรื่อง เช่น syntax ของ reference อ่านแล้วยังไม่เกิดความมั่นใจว่าเมื่อไรควรใช้แบบไหน ในขณะที่ quiz ซึ่งควรจะช่วยตรวจสอบความเข้าใจได้กลับมีหลายข้อที่ถามเกินเนื้อหาปัจจุบัน ทำให้วัดอะไรไม่ได้ และยังทำให้เกิดความไม่มั่นใจในสิ่งที่เรียน แต่หลายข้อที่ถามไม่เกินเนื้อหาก็ช่วยทบทวนได้ดีเหมือนกัน และบางข้อยังบ่งชี้ได้ว่าเนื้อหาที่อธิบายไปนั้นยังขาดรายละเอียดบางอย่าง จึงขอแนะนำผู้ที่จะเรียนจากแหล่งนี้ว่าพยายามทำ quiz เท่าที่ทำได้ แต่ถ้าทำไม่ได้เพราะข้อมูลยังไม่เพียงพอก็ไม่ต้องเสียกำลังใจ ข้ามๆ ไปบ้างก็ได้ แล้วค่อยกลับมาดูทีหลังเมื่อได้เรียนเพิ่มเติม
หลังจากเรียนมาได้ประมาณครึ่งหนึ่งของจำนวนบททั้งหมด ก็ขอเขียนบันทึกระหว่างทางสักหน่อยเกี่ยวกับประเด็นของ Rust ที่คิดว่าน่าสนใจ
Ownershipด้วยความรู้ครึ่งทางนี้ ผมได้เห็นความพยายามของ Rust ที่จะจัดการ memory แบบไม่ใช้ garbage collection โดยขจัดปัญหา memory leak หรือ memory corruption ต่างๆ ที่พบใน C/C++ โดยเฉพาะใน C โดยเครื่องมือหลักที่ใช้คือ ownership ที่ฝังไว้ในตัวภาษาเลย
เวลาเขียน C เราจะสร้าง contract ต่างๆ ของ API ว่า object ต่างๆ ที่สร้างขึ้น เป็นหน้าที่ของใครในการทำลาย โดยเขียนไว้ใน API documentation และเป็นเรื่องของนักพัฒนาที่ ควร พยายามทำตาม บางครั้งมีการถ่ายโอนความรับผิดชอบในการทำลายไปให้ object อื่นก็ควรทำให้ชัดเจน โครงการไหนที่มีการจัดการเรื่องนี้ดีก็จะปลอดปัญหา แต่ถ้ามีจุดผิดพลาด ก็กลายเป็นการเขมือบทรัพยากรระบบ หรือทำให้โปรแกรมตาย หรือกระทั่งโดนจารกรรมข้อมูลได้ โดยมีการพัฒนาเครื่องมือต่างๆ มาช่วยตรวจสอบ เช่น valgrind
ถ้าเป็น C++ ก็มี constructor/destructor รวมถึง smart pointer ต่างๆ มาช่วยจัดการให้ ก็สะดวกกว่า C ขึ้นมาหน่อย แต่ก็ยังอาศัย วินัย ของโปรแกรมเมอร์บ้างอยู่ดี การแยก composition/aggregation ออกจาก association ใน class diagram ก็เพื่อสื่อสารเรื่องนี้
แต่ Rust กำหนดเรื่องนี้ไว้ในตัวภาษาเลย เรียกว่า ownership และใช้ move semantics เป็นหลักใน assignment และการ pass argument ทำให้มีการถ่ายโอน ownership เพื่อลดการ clone โดยไม่จำเป็น โดยที่ยังมี reference คล้ายของ C++ ให้ใช้ในกรณีที่แค่ ยืมใช้ (borrow) โดยไม่ take ownership โดยทั้งหมดนี้มีการตรวจสอบให้ตั้งแต่ตอนคอมไพล์!
ตอนที่เรียนเรื่องนี้ก็รู้สึกว่าเป็นความกล้าหาญมากที่ Rust พยายามจัดการ memory ด้วยคอมไพเลอร์ และถ้าทำได้จริงโดยไม่เพิ่มความยุ่งยากเกินไปก็เป็นเรื่องดี แต่ในใจก็สงสัยว่าจะทำได้แค่ไหน มีอะไรต้องแลกแน่ๆ ซึ่งก็เป็นไปตามนั้น เยอะเสียด้วย มันเหมือนการเลือก program design แบบหนึ่งที่อาจมีในโครงการ C/C++ บางโครงการ แล้วบังคับให้ใช้แบบนั้นไปเลย โดยบัญญัติเป็น syntax และ semantics ของภาษา โปรแกรมเมอร์ภาษา Rust ซึ่งเรียกว่า Rustacean (มันคือ Crustacean หรือสัตว์พวกกุ้งกั้งปูที่ไม่เขียน C? :P) ต้องใช้โหมดความคิดอีกโหมดหนึ่งในการเขียนโปรแกรมไปเลย
และขอบอกว่าสมัยที่เรียน C นั้น ผมไม่เคยมึนกับ pointer ของ C เท่ากับที่มึน ownership ของ Rust เลย! ตอนที่เขียน C นั้น ownership ต่างๆ เกิดจากวินัยที่เราสร้างขึ้นเองจากความเข้าใจ แต่พอมันกลายเป็นข้อบังคับของภาษา มันจะเหมือนการเดินที่ต้องคอยระวังไม่ให้เหยียบมดหรือกระทั่งทำให้มดตายทางอ้อม ขนาดนั้นเลย
Safe Borrowsบ่อเกิดของความมึนคือ Rust มีการสับรางการยืมไม่ให้เกิด race condition โดยอนุญาตให้มีการยืมแบบอ่านอย่างเดียว (immutable borrow) ได้พร้อมกันหลายทาง แต่ทันทีที่มีการยืมแบบเขียนได้ (mutable borrow) เกิดขึ้น การยืมอื่นทุกทางจะหมดอายุทันที ไม่ว่าจะยืมแบบเขียนได้หรือไม่ได้ และถ้ามีการใช้การยืมที่หมดอายุแล้ว ก็จะเกิด error ทันที โดยเป็น error ที่ตรวจพบตั้งแต่ตอนคอมไพล์!
การตรวจสอบนี้ ช่วยป้องกันบั๊กอย่างเช่นมีการลบ element ในลูปที่ iterate ใน vector เพราะการลบ element ต้องยืม vector แบบ mutable ทำให้ iterator ที่กำลังยืม vector อยู่เหมือนกันไม่สามารถ iterate ต่อไปได้ หากต้องการลบ element ในลูปจริงๆ ก็คงต้องเลี่ยงไปใช้ indexing แทน ซึ่งปลอดภัยกว่า เป็นต้น
ฟังดูเหมือนไม่มีอะไร แต่เอาเข้าจริงเวลาอ่านโค้ดมันซับซ้อนว่ามีการยืมซ่อนอยู่ที่ไหน ใครหมดอายุตอนไหน เวลาทำ quiz จะมึนมาก แต่เวลาคอมไพล์จริง error message ช่วยได้เยอะมาก
Lifetimeใน C/C++ มีบั๊กประเภท dangling pointer หรือ use after free ที่เกิดจากการ dereference pointer ชี้ไปยัง object ที่ถูกทำลายไปแล้ว Rust ป้องกันปัญหานี้ด้วยการกำหนด lifetime ของ object ทำให้การใช้ reference ที่นอกจากไม่อนุญาตให้เป็น null แล้ว ยังไม่อนุญาตให้ reference ไปยัง object เกินอายุขัยของ object ด้วย ถ้าตรวจพบก็จะมี compiler error เช่นกัน!
จากสามเรื่องข้างต้น น่าทึ่งที่ Rust พยายามขนาดนี้เพื่อป้องกันปัญหา memory ตั้งแต่ตอนคอมไพล์ สิ่งที่แลกมาคือคุณต้องใช้ความระมัดระวังอย่างหนักในการเขียนโค้ด ซึ่งบางครั้งกฎเหล่านี้ยังครอบคลุมไปถึงกรณีที่พิสูจน์ได้ว่าปลอดภัยด้วย แต่ถ้ามันแหกกฎของ Rust ก็ถูกตีตกได้
ยังมีเรื่องอื่นที่ขัดกับความรู้สึกของคนที่มาจากภาษา C/C++
The Default Immutabilityในขณะที่ C/C++ และภาษาอื่นจำนวนมากใช้ ตัวแปร เพื่อการคำนวณ โดยปกติตัวแปรจึงสามารถเปลี่ยนค่าได้ ยกเว้นเมื่อต้องการห้ามเปลี่ยนจึงใส่ qualifier const กำกับ แล้วคอมไพเลอร์จะช่วยโวยวายให้ถ้ามีความพยายามจะเปลี่ยนค่าที่กำหนดเป็น const ไว้ แต่ Rust จะให้ตัวแปรเป็น const หรือ immutable โดยปริยาย เมื่อต้องการเปลี่ยนค่าจึงใส่ keyword mut (mutable) กำกับ นัยว่าเป็นการเอื้อต่อ functional programming และ concurrency กระมัง? แต่ในแง่หนึ่งก็เป็นการเน้นให้เห็นชัดเจนว่าค่าไหนมีโอกาสถูกเปลี่ยน เช่นตอนที่ให้ฟังก์ชัน ยืม object ไป อาจมีการเปลี่ยนค่าใน object นั้นกลับมา ซึ่งโค้ดที่เขียนด้วย C/C++ จำนวนมากที่ไม่ค่อยเคร่งครัดการใช้ const ก็จะเห็นเรื่องนี้ได้ไม่ชัด แต่กับ Rust ถ้าไม่เคร่งครัดคุณก็หมดสิทธิ์เปลี่ยนค่า ซึ่งก็อาจจะดี แต่คุณจะเผลอสับสนกับโหมดความคิดอยู่บ่อยๆ
The Default Move Semanticsใน C++11 มีสิ่งที่เรียกว่า move semantics ซึ่งใช้ย้ายข้อมูลจาก object หนึ่งไปยังอีก object หนึ่งที่เป็น class เดียวกันพร้อมกับล้างค่าใน object ต้นทาง ทำให้เกิดการย้าย ownership โดยไม่มีสำเนาเกิดขึ้น ซึ่งนอกจากจะช่วยจัดการ ownership แล้ว ยังช่วยลดการ clone และ destruct โดยไม่จำเป็นอีกด้วย เพราะ move constructor สามารถย้ายเฉพาะ pointer ที่ชี้ data ได้เลย ไม่ต้องสำเนาตัว data ใน object ใหม่ และทำลาย data ใน object เก่า โปรแกรม C ที่ออกแบบดีๆ ก็สามารถ move data ในลักษณะนี้ได้เช่นกัน แต่เรื่องนี้ก็ยังไม่ใช่ default ของทั้ง C และ C++ ต้องมีการทำอะไรบางอย่างเพิ่มเติมเพื่อจะใช้ เช่น ใน C++ ก็ใช้ std::move()
แต่ Rust ใช้ move semantics โดยปริยาย ทั้งใน assignment และ function call ยกเว้นว่าต้องการทำสำเนาจึง clone เอา หรือแค่ให้ยืมผ่าน reference เอา ซึ่งก็อาจทำให้โค้ดโดยรวมมีประสิทธิภาพขึ้น แต่คุณก็ต้องระวังผลของการเปลี่ยนแปลง ownership ที่เกิดขึ้นด้วย
นอกจากนี้ ก็มีหลายสิ่งที่เป็นประโยชน์ใน Rust
Enum as Tagged Unionเวลาใช้ union ใน C/C++ เรามักใช้ร่วมกับ field ที่ระบุว่าเราใช้ข้อมูลชนิดไหนใน union นั้น ซึ่งเคยเห็นบางตำราเรียกว่า tagged union
Rust เอาสิ่งนี้มารวมเข้ากับ enum โดยกำหนดให้ enum สามารถมีข้อมูลประกอบได้ ซึ่งหาก implement ด้วย C/C++ ก็คือการใส่ union ประกอบกับ enum เป็น tagged union นั่นเอง ทำให้สร้าง tagged union ใน Rust ได้สะดวกสบาย ไม่ต้องเขียนโค้ดในการอ่าน/เขียนเอง
Option, ResultRust ลงทุนสร้าง enum แบบมีข้อมูลประกอบ ก็นำมาใช้ประโยชน์กับค่า return ของฟังก์ชันที่สามารถ return ทั้งสถานะ success/failure และผลลัพธ์ที่ได้ในคราวเดียว
Option คือ enum ที่มีสองค่า คือ None กับ Some โดยค่า Some สามารถมีข้อมูลประกอบได้ ใช้กับฟังก์ชันที่ทำอะไรสักอย่างให้ได้ผลลัพธ์โดยอาจล้มเหลวได้ เช่น การค้นหาข้อมูล ในภาษา C/C++ เราอาจให้ฟังก์ชัน return สถานะ success/failure และถ้า success ก็เขียนข้อมูลลงใน parameter ที่ call by reference มา เช่น
bool Table::FindData(const char* key, Record& result) const;หรืออีกแบบคือ return pointer ไปยัง object ที่สร้างขึ้นใหม่ โดยถ้าล้มเหลวก็ return NULL
Record* Table::FindData(const char* key) const;แต่ Rust ไม่มี null pointer/reference แต่จะ return เป็น Option มาเลย
impl Table { pub fn find_data(&self, key: &str) -> Option<Record> { ... } }โดยผู้เรียกจะตรวจสอบค่า enum ที่ return มาก่อนดึงค่าออกมาใช้ก็ได้ หรือจะใช้ method ของ Option ในการแกะห่อเอาค่ามาใช้ก็ได้ ซึ่งเป็นวิธีที่ถือว่าดูดี
Result ก็คล้ายกัน คือเป็น enum ที่มีสองค่า คือ Ok กับ Err โดยค่า Ok สามารถมีข้อมูลผลลัพธ์ประกอบได้ และค่า Err ก็มีข้อมูล error ประกอบได้ (ต่างกับ None ของ Option ที่ไม่มีข้อมูลประกอบใดๆ)
Trait-Bounded Generictrait คือสิ่งที่คล้ายกับ interface ในภาษา OOP ต่างๆ แต่ไม่เหมือนกันเสียทีเดียว
generic ก็เหมือนกับ template ของ C++ ซึ่งหากใครเคยใช้ STL ของ C++ จะรู้ว่า type ที่จะมาเป็น parameter ของ template หนึ่งๆ มักมีข้อแม้บางอย่าง แล้วแต่ template ที่ใช้ เช่น ต้องเปรียบเทียบค่ากันได้ หรือต้องรองรับการ move ฯลฯ แต่ไม่เคยมีการประกาศชัดเจน จะรู้ได้ก็ตอนที่คอมไพเลอร์ฟ้องว่าขาดคุณสมบัติ แล้วค่อยไปหาทางอุดทีละเรื่องเอา (ผมว่านี่แหละคือสิ่งที่คล้ายกับ duck typing ใน C++)
แต่ generic ของ Rust สามารถประกาศคุณสมบัติของ type ที่เป็น parameter ได้ว่าต้องรองรับ trait ใดบ้าง ทำให้รู้แต่ต้นว่าต้องเตรียมคุณสมบัติอะไรไว้
Rust ยังมีรายละเอียดให้เรียนรู้อีกเยอะ นี่ผมเพิ่งมาได้ครึ่งทาง ก็ยังต้องเรียนรู้กันต่อไป
bact: เอดูเขต
มิตรสหายหลายคนมีปัญหากับสิ่งที่พวกเขาเรียกร่วมกันว่า “woke” (ส่วนจะมีความหมายร่วมกันแค่ไหน ผมเองก็ไม่ทราบ) บางคนก็ไม่ได้ถึงขนาดรังเกียจ แต่ถ้าไม่มีธุระด้วย เลี่ยงได้ก็เลี่ยง เพื่อความสงบสบายของชีวิต
นึกไปก็ทำให้รู้สึกว่า สักสิบปีนิดๆ ที่แล้ว สมัยเพิ่งเริ่มเรียนโทที่ไทย ข้าพเจ้าก็น่าจะนับเป็นอะไรที่เรียกว่า “woke” ได้ (ซึ่งไม่รู้ว่าความหมายเดียวกับมิตรสหายแค่ไหน) หรือตอนนี้อาจจะยังเป็นอยู่ด้วยซ้ำ
มิตรสหายร่วมรุ่นทั้งที่เรียนในชั้นเรียนเดียวกันและมิตรสหายโดยทั่วไปจากคณะอื่นหรือที่พอจะเห็นงานกันในพื้นที่ออนไลน์ มีลักษณะร่วมกันในตอนนั้นคือ ความตื่นเต้นกับสำนักคิด ทฤษฎี วิธีวิทยา ข้อค้นพบ เรื่องเล่าจากสังคมที่อยู่ไกลออกไป ฯลฯ ที่ได้เริ่มรู้จักกันในช่วงนั้น
“ความตื่น” นี้ นำไปสู่ความพยายามลองใช้สิ่งเหล่านั้นเป็นเครื่องมือในการคิดกับปรากฏการณ์ร่วมสมัยรอบตัว การแลกเปลี่ยนบนเว็บบอร์ดและการผลิตงานเขียนบนบล็อกส่วนตัว บล็อกกลุ่ม และสื่อออนไลน์อย่างประชาไท มหาวิทยาลัยเที่ยงคืน เป็นไปอย่างคึกคัก ยังไม่นับงานเสวนาต่างๆ ทั้งวงปิด วงเปิด ทั้งบนโต๊ะอาหารและบนเวที
การใช้คำใหญ่ๆ การอ้างถึงมโนทัศน์หรืออะไรที่ค่อนไปทางนามธรรมเพื่อจัดกลุ่มสิ่งของหรือปรากฏการณ์ การพิมพ์/พูดไทยคำนึง วงเล็บภาษาต่างประเทศอีกคำนึง (ถ้าจะให้ดีควรเป็นคำฝรั่งเศส [français] หรือเยอรมัน [Deutsch] เก๋ไปอีกขั้นคือรัสเซีย [русский язык]) การอ้างถึงสำนักคิดของนักปรัชญาคนนั้นคนนี้เพื่อจัดประเภทสายธารความคิดหรือแนวของข้อถกเถียง
ซึ่งหลายครั้งก็มั่วซั่วไม่น้อย กลับไปอ่านที่ตัวเองเขียนส่งเป็นการบ้านก็จะแบบ กูเขียนอะไรไปวะ
แต่ “คนรุ่นผม” ซึ่งไม่ได้หมายถึงคนอายุไล่ๆ กัน หากหมายถึงคนที่มีโอกาสก่อรูปความคิดขึ้นมาในช่วงใกล้ๆ กัน มีความโชคดีอยู่หลายอย่างในช่วงเวลานั้นคือ:
หนึ่ง ชีวิตมันช้ากว่าตอนนี้ หมายถึงจังหวะการโต้ตอบที่ช้ากว่าเรียลไทม์อยู่มาก
สอง แพลตฟอร์มที่เราใช้มันเอื้อให้เราสื่อสารกันด้วยข้อเขียนขนาดค่อนข้างยาว คำอธิบายและการยกตัวอย่างสามารถถูกมัดรวมอยู่ในหีบห่อเดียวกับข้อเสนอได้เลย
สาม เราอยู่ในแวดวงไม่ได้ไกลจากกันมาก มีทุนทางวัฒนธรรมบางอย่างร่วมกัน ซึ่งช่วยลดความเข้าใจผิดในการสื่อสารลงไปได้บ้าง
และ สี่ คือการที่การโยนไอเดียต่างๆ นั้นอยู่ในบริบทของการศึกษา (ทั้งในระบบและนอกระบบ) ซึ่งการถามเพื่อทบทวนคุณค่า หรือการลองเสนออะไรประหลาดๆ หรือดูโง่ๆ มันเป็นเรื่องที่ถูกอนุญาตให้ทำได้ หรือเป็นเรื่องที่ถูกคาดหวังว่าจะต้องทำด้วยซ้ำ
ลองตัดประโยคนึงมาจากข้อเขียนของใครสักคนในรุ่นนั้น มาอ่านในตอนนี้ มันต้องมีขำกันบ้างล่ะ
แต่มันสนุก มันคือการมีโอกาสได้ลอง
ความโชคดีอีกอย่างคือ ครูที่เราได้เจอในตอนนั้นก็ช่วยขีดเส้นใต้ ขีดฆ่า วงปากกาแดง เขียนเครื่องหมายคำถามตัวใหญ่ กระทั่งแก้ภาษาให้เป็นย่อหน้าๆ แดงเถือกไปทั้งหน้ากระดาษ
หรือบางทีก็พูดหน้าเรียบๆ กับเราว่า “เขียนแบบนี้ไม่ต้องเรียนหนังสือก็ได้”
(แล้วพวกเราก็ไปนั่งปรับทุกข์กันที่ร้านข้าวต้ม)
มันเป็นการทดลองที่มีคนช่วยแนะ แล้วเราก็ไปได้เร็วขึ้นจากคำแนะนำหรือข้อวิจารณ์เหล่านั้น ไม่ต้องงมเอง ไม่ต้องเสียเวลาหลงทางนานๆ เราเรียนรู้ระหว่างลงมือปฏิบัติ ปฏิบัติแบบงงๆ นี่แหละ และอีกวิธีที่ครูของเราช่วยให้เราเรียนได้เร็วขึ้น ก็คือการที่เขาทำให้เห็นว่าครูก็ยังเป็นนักเรียนได้ เพื่อให้เราลองเป็นครู
แต่นั่นอาจจะเป็นความหรูหราหรืออภิสิทธิ์สำหรับคนรุ่นเราเท่านั้น?
เพราะถ้าย้อนกลับไปพิจารณา “ความโชคดี” หรือลักษณะ 4 ประการของบริบทที่กล่าวมาข้างต้น ที่คนรุ่นผมผ่านกันมาในช่วงที่พวกเราก่อรูปความคิด มันตรงข้ามกับทวิตเตอร์ (ที่ถูกมองว่าเป็นฐานที่มั่นของชาว woke) หมดเลย
ทวิตเตอร์ และวิธีที่เราใช้และอยู่กับพื้นที่สื่อสารตอนนี้โดยทั่วไป มันเรียลไทม์ มันสั้น มีคนหลากหลายปนเป และไม่ได้ผลัดกันสวมบทนักเรียนและครู (อย่างไรก็ดี สิ่งเหล่านี้ไม่ได้ไม่ดีด้วยตัวเองมันเอง มันก็มีข้อดีของมัน)
สภาพแวดล้อมมันไม่ค่อยสนับสนุนให้ช่วยกันก่อรูปความคิดได้ และเราก็ไม่มีแรงด้วยแหละ (คือมันต้องมีวิธีแน่นอน แต่เราไม่มีแรงไป หรือไม่มีใจ ทำความเข้าใจและทดลองหาวิธีกับมันแล้วไง)
ถ้าไม่มีคนจ้างหรือไม่ได้รู้สึกว่าสิ่งนี้เป็นพันธกิจของชีวิต จะมีใครสักกี่คนในพวกเราที่จะอยากรับ “ภาร(ะ)กิจ” ในการร่วม “เอดูเขต” ชาว woke (สมมติว่าเราคิดว่ายังเอดูเขตกันไปกันมาไหว) รถคันไหนบนทางด่วนทำอะไรงกเงิ่นก็ด่าก่อนเลยละกัน ลืมว่าคนสมัยนี้อาจไม่เหลือถนนในหมู่บ้านหรือเว็บบอร์ดให้ไปหัดขับแล้ว ทุกคนต้องมาหัดขับบนถนนสาธารณะกันหมด (สาธารณะมากสาธารณะน้อย ก็แล้วแต่จำนวนรีทวีตหรือมิตรรักนักแคป)
แต่นั่นก็อาจจะอธิบายสภาพโดยทั่วไปของคนรุ่นผมก็ได้มั้ง ว่า โอเค กูพอแล้วกับโลก ไม่หวังอะไรมากไปกว่านี้ ไม่มีแรงไปวงปากกาแดงหรือเขียนคอมเมนต์อะไรให้ใครแล้ว นับจากนี้ขอใช้ชีวิตอย่างสงบ ซึ่งก็สมควรได้รับสิทธิ์นั้น พวกมึงก็เหนื่อยกันมาเยอะแล้ว
ส่วนมิตรสหายท่านใดที่อยากจะส่งต่อโอกาสที่เคยได้รับมา ก็ขอให้มีแรง กินน้ำ กินขนม ทำใจร่มๆ