เชื่อมต่อ Clojure REPL เข้าไปที่ VM ที่ทำงานอยู่

คนที่เขียน Java มาอาจจะเคยต้อง restart server เป็น debug mode เพื่อต่อ debugger ของ IDE เข้าไป debug ปัญหา โดยมีความยุ่งยากคือต้อง restart ซึ่งอาจจะรบกวนการใช้งานของ user หรือบางครั้งหลังจาก restart แล้วปัญหาก็ดันหายไป

คนที่เขียน Ruby มาอาจจะเคย start irb บน server เพื่อลองรันคำสั่งต่างๆ แต่มันก็เป็นคนละโปรเซสกับที่ server ทำงานอยู่

บน Clojure เราสามารถเปิด port เอาไว้เชื่อมต่อ repl เข้าไปที่ VM หรือ server ที่ทำงานอยู่ได้ง่ายๆ โดยใช้ฟีเจอร์ที่ชื่อว่า Socket Server ซึ่งถูกเพิ่มเข้ามาใน Clojure เวอร์ชั่น 1.8

การใช้งานทำได้โดยการเพิ่ม option นี้เข้าไปเวลา start JVM

-Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}"

* :accept คือ ฟังก์ชันที่จะถูกเรียกเมื่อมีการติดต่อเข้ามาทาง socket

ในที่นี้ผมขอยกตัวอย่างรัน repl ปกติขึ้นมาหนึ่ง vm พร้อมเปิด socket ให้เชื่อมต่อ repl เข้ามาเพิ่มได้อีกละกัน

ปกติผมจะ start repl ด้วยคำสั่งนี้

$ rlwrap java -jar /path/to/clojure.jar

เราก็ทำการเพิ่ม option ข้างต้นเข้าไป

$ rlwrap java -Dclojure.server.repl="{:port 5555 :accept clojure.core.server/repl}" -jar /path/to/clojure.jar

เมื่อทำการรันคำสั่งด้านบนนี้ repl จะถูก start ขึ้นมาปกติ เมื่อ start เสร็จแล้ว เราสามารถ connect เข้าไปที่ VM นั้นผ่าน socket ได้โดยใช้ telnet ดังนี้

$ telnet localhost 5555

เราก็จะได้ repl ขึ้นมาอีกอันซึ่ง share data และ memory เดียวกับ repl แรกทั้งหมด เราสามารถทำทุกอย่างได้ตามชอบใจกับ VM นั้น

รายละเอียดเพิ่มเติมที่ Launching a Socket Server

เขียนครั้งแรกที่ https://www.facebook.com/groups/clojurethai/permalink/1231413573563800/

ทำไม Clojure ไม่ใช้ class ในการ model domain

การปรับตัวสำคัญอย่างหนึ่งสำหรับคนที่เริ่มใช้ Clojure คือ Clojure แนะนำให้ model domain ด้วย data structure ไม่ใช่สร้าง class เหมือนตอนเขียนภาษา OOP

Rich Hickey อธิบายเรื่องนี้ในวิดีโอนี้ได้ดีมาก เค้าบอกว่าเรื่องนี้เป็นส่วนสำคัญอย่างมากที่ทำให้โค้ดที่เขียนด้วย Clojure สั้นกว่า https://www.youtube.com/watch?v=VSdnJDO-xdg&t=2869 (นาทีที่ 47:49 – 54:15)

class ทำให้เกิด specific method ที่เราต้องรู้จักเพิ่มขึ้นอีกมากมาย (Rich แซวว่านี่แหละสาเหตุที่ autocomplete ของ IDE จึงสำคัญมากสำหรับการเขียนภาษาอย่าง Java, C#)

ในขณะที่ฝั่ง Clojure ใช้ data structure ทำให้เราสามารถใช้ collection library ที่มีอยู่ทั้งหมด ที่เรารู้จักและคุ้นเคยในการจัดการกับ data model นี้ ทำให้เราลดการเขียนโค้ดใหม่ขึ้นมาจัดการโดยเฉพาะไปได้

Rich ยกตัวอย่าง HTTP request ซึ่งอันนึงถูก wrap ด้วย Java class HttpServletReq ที่มีเมท็อดเยอะมาก และมีเมท็อดที่ทำงานใกล้เคียงกันอยู่เป็นจำนวนมากที่ถูกใส่เข้ามาเพื่อให้ใช้งานได้ง่ายขึ้น

ในขณะที่ Clojure ใช้ map ธรรมดา ฟังก์ชันที่มีอย่าง get,get-in,assoc,assoc-in,update-in และอีกมากมาย (ใต้หัวข้อ Collections, Sequences ในหน้านี้ http://clojure.org/api/cheatsheet) สามารถนำมาใช้กับ data model ใหม่ของเราได้ทันที

Alan Perlis – Turing Award คนแรกกว่าไว้ว่า
“It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures.”
http://www.cs.yale.edu/homes/perlis-alan/quotes.html

การ model domain เป็น data structure ยังมีข้อดีอื่น ๆ อีก อย่างเช่น
– สร้างง่าย ใครเคยต้อง new class ที่ต้องการ parameter มากมายและใน constructor มีการทำงานเยอะ น่าจะพอเห็นภาพ
– debug ง่าย อยากดูอะไรก็ print มันออกมาก เห็นทุกอย่างไม่ต้องไป override เมท็อด .toString
– serialize/deserialize ง่าย ทำให้ persist และส่งผ่าน network ง่ายด้วย ไม่ต้องเขียน serializer/deserializer ของทุก class ที่จะส่งผ่าน network ให้ตรงกับรูปแบบที่ data transfer format รองรับ

โพสครั้งแรกที่ https://www.facebook.com/groups/clojurethai/permalink/1216890918349399/

ควรเขียนโค้ดใหม่บ่อย ๆ

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

ผู้พูด คือ Chad Fowler อดีต CTO ของ Wunderlist ที่ถูก Microsoft ซื้อไป ตอนนี้เค้าเปลี่ยนตำแหน่งไปเป็น General Manager

หัวข้อจริงๆ ของ talk คือ Impermanance – the ironic key to systems that survive โดยในภาพรวมเค้าเล่าถึงระบบของ Wunderlist ที่ก่อนเค้าเข้าไปทำงานเป็น Monolith Ruby application แล้วตอนนี้กลายเป็นระบบที่เป็น microserivces ซึ่งมีหลักหลายร้อย service

เค้ากล่าวว่า “The system is the asset. Code is a liability.”

เค้าเปรียบเทียบการที่ร่ายกายคนเราอยู่มาได้จนถึงวันนี้ เกิดจาก cell แต่ละส่วนในร่างกายตายและถูกสร้างขึ้นมาใหม่อยู่ตลอดเวลา

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

ระบบที่รันอยู่นานและไม่เคยเปลี่ยนเลยพอถึงเวลาที่เราจะต้องเปลี่ยนมันจะเปลี่ยนยาก ซึ่งเป็นความเสี่ยง ถ้าเราเปลี่ยนมันอยู่บ่อย ๆ เราจะมีความมั่นใจที่จะเปลี่ยนจะแก้ไขมันอยู่เสมอ

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

เค้าเล่าว่าหลังจากเปลี่ยนเป็น microservice ในปีแรก โค้ดประมาณ 80% ถูกเขียนใหม่ ไม่ได้ถูกเขียนใหม่เพราะแค่เขียนใหม่ แต่เขียนใหม่เพราะต้องบำรุงรักษา ปรับปรุงหรือเพิ่มฟีเจอร์ให้แก่มัน

เค้ามีนโยบายให้ทีมงานเลือกใช้ภาษาอะไรก็ได้ แม้กระทั่งเด็กฝึกงาน ขอแค่เข้ากับระบบ deploy และระบบ metric ที่มีอยู่ได้ ประโยชน์ที่ได้รับคือ ไม่มีปัญหาเมื่อจะต้อง upgrade อะไร เพราะทำอยู่เสมอ ๆ อยากลองใช้ภาษาอะไรก็ทำได้ง่าย เค้าเคยมีเหตุการณ์ระบบมีปัญหาตอนกลางคืน และคนที่อยู่เวรวันนั้นอ่าน Haskell ไม่รู้เรื่องเลยเขียนใหม่เป็น Elixir เพื่อแก้ปัญหาตอนนั้นซะเลย

อีกนโยบายหนึ่งของเค้า คือ no shared code ระหว่าง component เพราะมันจะทำให้การแก้ไขแต่ละ component กระทบกันทำให้เกิดการเปลี่ยนแปลงยาก ให้ใช้ share service แทน

ระบบของเค้าใช้การ communication ในรูปแบบง่าย ๆ ได้แก่ http/json เป็นส่วนหนึ่งที่ทำให้เขียน component ใหม่ได้ง่าย เค้าบอกว่าระบบที่เข้าใจง่ายสำคัญกว่าระบบที่มีประสิทธิภาพ (แต่สุดท้ายระบบที่เค้าเขียนให้เข้าใจง่ายนี้ก็เร็วกว่า monolith ของเก่ามากอยู่ดี)

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

ระบบของเค้าไม่เน้นใช้ testing ในการควบคุมคุณภาพของระบบ แต่เน้นที่การ monitoring, metric ที่ครอบคลุมอยู่ทั้งระบบ เช่น ถ้าผู้ใช้งาน sign-in ไม่ได้เค้าจะรู้ทันที เค้ากล่าวว่าเราป้องกันปัญหาทุกอย่างไม่ได้ เราจึงควรหาวิธีที่ทำให้รู้ปัญหาได้อย่างรวดเร็วและสามารถแก้ไขมันได้อย่างรวดเร็ว

มีครั้งนึงที่ลูกทีมของเค้าอยากให้วางกรอบเลือกใช้ซักแค่ไม่กี่ภาษาก็พอ ซึ่งในตอนแรกเค้าก็ดีใจ เพราะเค้าไม่ต้องเป็นคนพูดเอง เมื่อลูกน้องพูดด้วยตัวเองก็แปลว่าพวกเค้าเต็มใจทำ แต่สุดท้ายแล้วเค้าก็มาคิดว่ามันจะทำให้กลับไปเจอปัญหาเก่า ๆ เช่น upgrade version ยาก หรือสุดท้ายบริษัทกลายเป็นที่ ๆ เขียนกันอยู่ภาษาเดียว เช่น Scala แล้วก็จะมีปัญหาเมื่อวันหนึ่งก็ต้องมาใช้ C# เพราะถูก Microsoft ซื้อ

“Small is not a goal”
“Healthy, longevity is the goal”
“Systems that survive are made of components that can change”
“If it hurts, do it more often” (เอามาจาก XP) ถ้าอะไรที่มันต้องเปลี่ยนอยู่แล้วก็เปลี่ยนมันบ่อย ๆ และหาทางทำให้การเปลี่ยนแปลงนั้นมันไม่รุนแรงและเปลืองทรัพยากร
Impermanence -> Decoupling, Longevity, Agility

คำถามช่วงท้าย

Q: การมีหลายภาษาแล้วไม่ expert มีผลไหม?
A: ปกติภาษาที่มีใช้ก็จะมี expert อยู่ในทีม แต่ถ้าเค้าไม่อยู่แล้วก็เขียนใหม่

Q: มีปัญหาด้านความรู้สึกภายในทีมไหมกับการที่ต้องทิ้งโค้ดตลอดเวลา เปลี่ยนแปลงตลอดเวลา (จากบทสนทนาและเสียงผมค่อนข้างมั่นใจว่าคนถามคือ Dave Thomas)
A: มี ต้องพยายามสร้างความเข้าใจถึงประโยชน์ และ celebrate การเปลี่ยนแปลงอยู่เสมอ ๆ

— x —

ใครอ่าน/ฟังเรื่องนี้แล้วสนใจ ผมขอแนะนำบล๊อกโพสซีรียส์เรื่อง The New Normal ครับ จากคนละคนกันแต่วิธีคิดคล้าย ๆ กัน

http://blog.cognitect.com/?tag=NewNormal+Series

เป็นแนวคิดที่เปิดหูเปิดตาดีอยากให้ได้อ่านกัน เคยเขียน email ไปขอแปลแล้วเค้าบอกว่าไม่ให้ republish

Clojure กับการพิสูจน์ตัวเอง

ผมเคยมีปมในใจอยู่เรื่องหนึ่ง การหัดใช้งานภาษา Clojure จนอยู่ในระดับที่ใช้งานได้อย่างมั่นใจช่วยผมแก้ปมในใจนั้นได้

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

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

หลังจากที่ผมทำงานไปได้ซักพัก ผมเริ่มหาภาษาเขียนโปรแกรมอื่น ๆ มาศึกษา อันเนื่องมาจากเหตุผลหลายอย่างที่ขอยังไม่เล่าในตอนนี้เพราะเดี๋ยวจะออกนอกเรื่องไปไกล ตอนนั้นผมเลือกภาษา Scala เป็นภาษาที่ผมจะหัดเป็นภาษาต่อไป เพราะผมเคยเรียน Functional programming ด้วยภาษา Haskell มาบ้างสมัยเรียนในมหาวิทยาลัยซึ่งผมก็ไม่ค่อยเข้าใจหรอก ที่เลือก Scala เพราะตอนนั้นมันโฆษณาว่ามันเป็นภาษา hybrid ที่รวมทั้งแนวคิด Object-Oritend programming (OOP) และ Functional programming (FP) เข้าด้วยกัน ตอนนั้นผมคิดว่าการเอาสองแนวคิดมารวมกันมันต้องเจ๋งมากๆ เลย ภาษาที่เขียนได้สองแบบก็น่าจะได้เปรียบ ผมโหลดหนังสือ Programming in Scala เถื่อนมาปรินท์อ่าน cover to cover จนจบ เข้าใจบ้างไม่เข้าใจบ้าง พอจบแล้วผมเริ่มหาโจทย์มาหัดเขียน ตอนนั้นผมเลือกหัดเขียนเว็บ โดยผมศึกษา Lift ซึ่งเป็นเว็บเฟรมเวิร์คของ Scala ที่ดังในสมัยนั้น ผลปรากฏณ์ว่าไม่ประสบความสำเร็จ มันยากไป ผมไม่เข้าใจอะไรเลย

ผมไปโหลดวิดีโอสอน Haskell ของ Erik Meijer มาดูหวังว่าจะได้เข้าใจพื้นฐาน FP เพื่อจะเอาไปรวมกับความรู้ OO ที่พอมีอยู่บ้างอาจจะทำให้เข้าใจ Scala ได้มาขึ้น ผมดูวิดีโอถึงเรื่อง Functor แล้วผมก็รู้สึกว่าผมไม่เข้าใจเลย และก็หยุดดูไป

หลังจากนั้นผมได้มีโอกาสทำงานที่ใช้ภาษา Ruby ร่วมกับทีมที่ทำ pair programming กันเป็นประจำ ซึ่งทำให้ผมศึกษาภาษา Ruby ได้อย่างรวดเร็วมาก ตอนนั้นผมคิดว่าทำไม Rails มันเข้าใจง่ายกว่า Lift ขนาดนี้ เพียงแค่เวลาประมาณ 1 ปีผมรู้สึกว่าผมเข้าใจ Ruby มาก ๆ

เมื่อรู้สึกมั่นใจในการเขียนภาษา Ruby แล้ว ผมเริ่มมองหาภาษาต่อไปที่จะศึกษาอีกครั้ง ผมเห็น blog อันหนึ่งมีคนเค้าทดลองเขียนและเปรียบเทียบระหว่างภาษา Scala กับ Clojure ซึ่งในตอนแรกที่ผมอ่าน ผมมั่นใจว่าผลลัพธ์จะต้องออกมาเป็นว่า Scala ดีกว่าแน่นอน แต่ blog นั้นก็ทำให้ผมประหลาดใจเพราะในตอนท้ายผู้เขียนสรุปว่าเค้าชอบภาษา Clojure มากกว่า ตอนนั้นผมคิดว่าภาษา syntax ประหลาดนี่อะนะ ภาษาที่ใช้ syntax เหมือนภาษาที่เกิดตั้งแต่ 40-50 ปีที่แล้วนี่เหรอ จะดีกว่าได้ยังไง ภาษา paradigm เดียวจะดีกว่าภาษา 2 paradigm ได้ยังไง

จุดสำคัญต่อมาที่ทำให้ผมสนใจภาษา Clojure คือมันมีหนังสือที่ชื่อ Joy of Clojure ผมอยากรู้ว่าความ joy นี้มันเป็นอย่างไร แม้กระทั่งภาษา Ruby ที่เค้าบอกว่า optimize for programmer happiness ยังไม่เห็นมีคนเขียนหนังสือ Happy Ruby เลย แล้ว Clojure นี่มันจะ joy ได้ซักขนาดไหน ผมเป็นคนที่แสวงหาความสุขจากการเขียนโปรแกรมอยู่เสมอ อันนี้ก็ขอเก็บไว้เอาไปเขียนได้อีกเรื่องหนึ่งเหมือนกัน ผมตัดสินใจซื้อหนังสือ Joy of Clojure มาอ่าน(รอบนี้ผมซื้อแล้ว😄) ผมอ่านไปได้ประมาณ 1 บทและเปิดข้าม ๆ จนจบเล่ม ผมหาความ joy ไม่เจอ ผมไม่เข้าใจความ joy ของ Clojure ผมไม่เข้าใจ Clojure

ลองนึกย้อนกลับไป ผมพบว่าผมไม่เคยหัดเขียนภาษาไหนได้ประสบความสำเร็จด้วยตัวเองเลย ตั้งแต่ VB, Scala, Haskell มาจนถึง Clojure ภาษาที่ผมเขียนได้อย่างมั่นใจมีแต่ภาษาที่มีคนสอน* อย่างเช่น Java กับ Ruby ผมไม่มีความสามารถในการศึกษาหาความรู้ด้วยตัวเองเหรอ ผมอิจฉาหลาย ๆ คนที่ผมมีโอกาสได้รู้จัก เค้าเหล่านั้นเปลี่ยนอาชีพมาเป็นโปรแกรมเมอร์ทั้ง ๆ ที่ไม่ได้เรียนจบสายคอมพิวเตอร์มา เค้ายังทำได้และเป็นโปรแกรมเมอร์ที่เก่งอีกด้วย ผมทึ่งและอิจฉาในความสามารถของคนกลุ่มนี้มาก ไม่ใช่แค่เรื่องเขียนโปรแกรมเพียงอย่างเดียวผมยังชื่นชมคนที่หัดทำสิ่งยาก ๆ ด้วยตัวเอง (self learner) อย่างอื่น อย่างเช่นการหัดเล่นเครื่องเล่นดนตรีอีกด้วย ผมอยากทำแบบนี้ได้บ้าง ผมอยากพิสูจน์ได้ว่าตัวผมมีความพร้อมที่จะใช้ชีวิตบนโลกนี้ต่อไป โดยไม่ต้องรอให้ใครมาป้อนอะไรให้ ผมอยากที่จะสามารถหัดและทำในสิ่งที่ผมอยากทำ อยากรู้ได้ด้วยตัวเอง

ผมจึงตัดสินใจใช้ภาษา Clojure เป็นการพิสูจน์ตัวเอง เนื่องจากมันมีความต่างและผมรู้สึกว่ามันยากเพียงพอที่ถ้าผมทำได้สำเร็จผมจะมั่นใจว่าผมไม่ได้โกงโดยการเอาสิ่งง่าย ๆ มาหัด ผมซื้อหนังสือ Programming Clojure มาอ่าน ผมทำแบบฝึกหัด 4clojure ซึ่งเป็นแบบฝึกหัดที่ทำให้ผมเริ่มมองเห็นความ joy ในภาษา Clojure โดย ณ เวลานั้นผมชอบเขียนภาษา Ruby มาก หนึ่งในฟีเจอร์ของ Ruby ที่ผมชอบมากที่สุดคือ การใช้งานเมท็อดต่าง ๆ ใน class Enumerable เช่น map, select, inject, group_by และอื่น ๆ อีกมากมาย ซึ่งจริงๆ แล้วมันคือ กลุ่มของเมท็อดที่ได้รับอิทธิพลมาจาก FP ที่พอได้มาทดลองแก้ปัญหาใน 4clojure แล้ว พบว่าสิ่งที่ผมชอบใน Ruby มันเก่งยิ่งกว่าขึ้นไปอีกบน Clojure  ทุกอย่างสั้น กระชับ และเชื่อมต่อกันได้อย่างแนบเนียนมาก และผมก็ติดใจภาษา Clojure ตั้งแต่นั้นเป็นต้นมา

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

•ขณะที่เขียน blog  นี้อยู่ผมนึกขึ้นมาได้ว่าจริง ๆ แล้วก่อนหน้า Clojure มีอีกภาษาหนึ่งที่ผมหัดเขียนเองจนถือว่ามั่นใจระดับหนึ่ง คือ Prolog แต่ตอนนั้นผมได้ใช้ Prolog แค่เพียงเวลาสั้น ๆ ผมจึงยังไม่รู้ไม่มั่นใจว่าตัวเองเข้าใจ Prolog เพียงใด ภาษานี้เลยหายไปจากความคิดในตอนที่ตัดสินใจหัด Clojure

How Clojure teaches me about side-effects

ดัดแปลงมาจากคอมเมนต์นี้ https://www.facebook.com/groups/919377878100706/permalink/1121787907859701/?comment_id=1121803797858112&reply_comment_id=1121826181189207

หลังจากที่ผมโพสว่า Clojure สอนผม ว่าผมจะอยู่ร่วมกับ side-effects ได้อย่างไร

พี่มาคอมเมนต์ Patraphong Sukhonpitumart ถามว่า

clojure สอนยังไง มีตัวอย่างมั้ยครับ

ผมเลยตอบว่า

ที่ผมพอจะนึกออกตอนนี้ ไม่ได้ exclusive สำหรับ clojure นะครับ

  • สอนให้ model domain เป็น value ไม่ใช่ object มันเหมาะกับแนวคิด bounderies (Functional Core, Imperative Shell) มาก
  • แยกที่เก็บ mutable data (atom) ให้ชัดเจน เราจะเรียกใช้แก้ไขมันเมื่อไหร่ก็ได้ แต่ก็จะบอกว่าพยายามให้มีให้น้อยที่สุด
  • ไม่มีคนทำ ORM แล้วประสบความสำเร็จ
  • Referential transparency จะใช้ไม่ได้ถ้ามี side-effects ฟังก์ชันอย่าง memoize ก็จะทำงานผิด ถ้าไม่สนใจเรื่องนี้
  • ความที่เราสามารถทำ side-effect ในฟังก์ชันได้ตามใจ แต่เค้าก็จะบอกว่าไม่ควรทำ เช่น ฟังก์ชันที่ใช้ update ค่าให้ atom (เพราะมันไม่การันตีว่าจะถูกเรียกแค่ครั้งเดียว)
  • ไม่สร้างความยุ่งยากให้เราหากเราจะทำ side-effects ง่ายๆ เช่น เราอยากจะ println เมื่อไหร่ก็ได้ ไม่โดน type บังคับ
  • พวก React wrapper ก็จะบอกให้เราระวังเรื่อง state โดยที่บางตัวก็ strict บางตัวก็ปล่อยหลวมๆ
  • def เป็น mutable เพราะมันไม่จำเป็นต้อง immutable
  • do, doseq ทำให้เรารู้ว่าเราทำ side-effects อยู่นะคือ ผมมีโอกาสเลือกอยู่เสมอว่าผมจะทำ side-effects ได้ แต่ผมก็จะเห็นชัดเจนว่าถ้าผมทำแล้วปัญหาที่ตามมาคืออะไร

Blending functional programming with imperative. Higher-level of abstraction or functional core.

ดัดแปลงและเพิ่มเติมจากคอมเมนต์คำถามในโพสนี้ https://www.facebook.com/nuttanart/posts/1015437440718608 เป็นคำถามจาก Supanat IBoss Potiwarakorn

ผมได้ยินคนพูดถึง functional core, imperative shell มาซักพักและ แต่ผมก็มักจะได้ยินว่า functional เนี่ยเป็น higher level abstraction เราแอบเอาลำดับขั้นตอนการทำงานต่างๆ ไปแอบไว้ภายใต้ declarative code ผมก็เลยยังรู้สึกสับสนอยู่เล็กน้อย (รู้สึกว่าเห้ย ถ้าเป็น shell มันต้องอยู่บนกว่าสิ)

เท่าที่ผมเข้าใจคือ monad เนี่ยมันคือเหมือน imperative shell ซึ่งจริงๆ แล้วมันก็คือ เรา แอบ state mutation เอาไว้ ไอสองเรื่องที่ผมพูดอ้างถึงบนนี่มันไม่ได้ขัดแย้งกัน แต่มันแค่อธิบายในคนละมุมกันถูกมั้ยฮะ

คำตอบของผม

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

ตอนนี้มีสองเรื่อง
a) functional เป็น declarative (higher-level abstraction) อยู่ด้านบน ส่วน imperative (lower-level abstraction) อยู่ด้านล่าง อย่างเช่นเราเขียน map,reduce โดยไม่ต้องวนลูปเอง
b) imperative อยู่ด้านบน functional อยู่ด้านล่าง ตามแนวคิด Functional Core, Imperative Shell เช่น เอา UI, http request, database ไว้ด้านบน หรืออย่าง Actor  model

เวลาเราเขียนโปรแกรมจริงๆ เราจะไม่ได้คำนึงถึงข้อ a) อะเพราะไม่ใช่หน้าที่เราจะต้องไปสนใจการทำงานตรงนั้น platform มีหน้าที่การันตีให้เรา ด้านล่างของเรามันไม่ functional อยู่แล้ว แต่ platform ทำให้เรารู้สึกว่าเราควบคุมทุกอย่างได้ในลักษณะ functional (input – output) แต่สำหรับ b) เราต้องสนใจเพราะเป็นสิ่งที่เราต้องเป็นคนสั่งให้มันทำ เพราะฉะนั้นแล้วในชั้นของเราที่ใช้ภาษาเขียนเพื่อแก้ปัญหาอื่นๆ เราคงโฟกัสที่ b) มากกว่า ส่วน a) สำหรับเราน่าจะสำคัญแค่ตอนที่เราอธิบายให้คนอื่นฟังว่า functional มันทำงานยังไงแค่นั้นมั้ง

จริงๆ แล้วเราอาจจะต้องไปเขียน imperative ด้านล่างบ้างก็ได้ แต่เราก็ควรห่อให้ imperative นั้นเรียบร้อยอยู่ภายในแล้ว expose ออกมาเป็นแค่ functional ให้ด้านบนใช้ ผมเชื่อว่าการที่ด้านล่างของเราเรียบง่ายและตรงไปตรงมาจะทำให้เราสามารถเข้าใจระบบโดยรวมของเราได้ง่ายกว่า จะเพิ่มฟีเจอร์ก็มั่นใจ จะ debug ก็ไล่ได้ง่าย

ผมชอบประโยคนี้ของ José Valim (ผู้สร้าง Elixir)

I often summarize functional programming as a paradigm that forces us to make the complex parts of our system explicit

ผมคิดว่าการที่เราเอา imperative ซึ่งเป็นส่วนที่ยากขึ้นมาไว้ด้านบน (ไม่ซ่อนอยู่ด้านล่าง) เป็นตัวอย่างที่ดีมากของการทำให้ complex parts explicit

ส่วนเรื่อง monad นี่ไม่รู้เลยอะ(เขียน clojure ไม่ต้องรู้จัก monad 😜) แต่ก็เคยได้ยินมาบ้างเหมือนกันที่ว่ามองมันเป็น imperative shell ได้

เรื่องสำคัญที่หนังสือ OOP ทั่วไปไม่ได้สอน

ดัดแปลงจาก Facebook โพสนี้ https://www.facebook.com/nuttanart/posts/10154374407186081

ซึ่งเป็นโพสต่อเนื่องจากโพสแนะนำหนังสือ 99 Bottles of OOP อันนี้ https://www.facebook.com/nuttanart/posts/10154374324641081

ถึงแม้ผมจะเชียร์หนังสือ 99 Bottles of OOP อย่างไร ผมยังรู้สึกว่ายังมีปัญหาสำคัญมากๆ อันหนึ่งที่ทำให้โค้ดเราเน่าและหนังสือเล่มนี้คงจะไม่ได้พูดถึง หนังสือเล่มอื่นๆที่ผมเคยได้อ่านมาก็ไม่ได้สอน คือ การอยู่ร่วมกันระหว่าง OO กับสิ่งเหล่านี้
– การเชื่อมต่อ database
– การทำ external call
– logging
– อ่าน/เขียนไฟล์
– UI
– ฯลฯ

อาจจะมีแนวคิด Clean Architecture ที่พูดถึงอยู่บ้าง เพียงแต่ว่างบางครั้งเราไปโฟกัสที่ตัว pattern และแต่ละ component มากเกินไปจนลืมว่าจริงๆ แล้วต้นเหตุของปัญหาของเราคืออะไร

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

บางคนอาจจะคิดในใจว่า นี่มัน side-effects นี่หว่า ใช่แล้วครับ ผมคิดว่าอีกปัจจัยหนึ่งที่จะทำให้เราเขียนโค้ด OO ได้ดี ดันเป็นสิ่งที่ OO ไม่ได้สอน แต่มันอยู่ในแนวคิด functional programming ถ้ารู้ว่าจะจัดสิ่งเหล่านี้ให้อยู่ถูกที่ถูกทางได้อย่างไร มันจะทำให้เราจะมีพื้นที่ให้จัดโค้ด OO ให้ดีได้

ลองไปศึกษาเรื่อง side-effects ดูนะครับ แล้วจะพบว่าหลายๆ ปัญหาที่เราเจอกันอยู่ สาเหตุเกิดจากพวกมันนี่แหละ

Update: talk นี้อธิบายเพิ่มเติมได้ดีว่า side-effects คืออะไร และมันสำคัญอย่างไร