- John Carmack ได้แชร์มุมมองส่วนตัวเกี่ยวกับการใช้ mutable variable
- เขากล่าวว่า การใช้ Python ทำให้ละเลยหลักการ ‘single assignment’ ไปบ้าง และควรระวังเรื่องนี้กับตัวเอง
- เขาเน้นว่า ควรหลีกเลี่ยงการกำหนดค่าซ้ำหรืออัปเดตตัวแปร ยกเว้นการคำนวณซ้ำในลูป
- หากเก็บขั้นตอนการคำนวณระหว่างทางไว้ทั้งหมด จะ ช่วยในการดีบัก และเมื่อย้ายบล็อกโค้ดก็ ช่วยป้องกันปัญหาที่มีการใช้ค่าก่อนหน้าโดยไม่ตั้งใจ
- เขาอธิบายว่าใน C/C++ การประกาศตัวแปรเกือบทั้งหมดเป็น const ตั้งแต่ตอนกำหนดค่าเริ่มต้นเป็นนิสัยที่ดี
- ท้ายที่สุด เขาย้ำว่า “อยากให้ mutable เป็นคีย์เวิร์ด” เพื่อให้ความไม่เปลี่ยนแปลงเป็นค่าเริ่มต้น
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
หลังจากใช้ Clojure มา 2 ปี ก็รู้สึกว่ามันยากมากที่จะอธิบายให้คนอื่นเข้าใจถึง ความชัดเจน ที่ความไม่เปลี่ยนแปลงมอบให้
คนที่คุ้นกับการคิดแบบทำให้เกิดผลผ่านการเปลี่ยนสถานะ มักจะเข้าใจได้ยากหากยังไม่เคยลองด้วยตัวเอง
ตัวอย่างเช่น ถ้าเขียน
x = 7; x = x + 3; x = x / 2ต่อให้สลับลำดับแล้วไม่เกิด error ผลลัพธ์ก็จะเปลี่ยนแต่ถ้าใช้ตัวแปรใหม่อย่าง
x1,x2ความผิดพลาดจากลำดับที่ไม่ถูกต้องจะปรากฏออกมาชัดเจนสุดท้ายแล้ว single assignment คือวิธี แสดงการพึ่งพาเหล่านี้อย่างชัดแจ้ง
ต่อให้พยายามอธิบายให้เพื่อนร่วมงานที่ไม่เคยใช้ภาษาฟังก์ชันเข้าใจว่าแนวคิดแบบยึดฟังก์ชันเป็นศูนย์กลางนั้น ทดสอบง่ายและสะอาดแค่ไหน ก็ยังไม่ค่อยเห็นภาพกัน
Python เขียนสไตล์ฟังก์ชันให้อ่านง่ายได้ยาก ส่วน JS กลับรู้สึกว่าทำได้ดีกว่า
สุดท้ายมีแค่ นักพัฒนาที่ช่างสงสัย เท่านั้นที่มักจะลองภาษาอย่าง Clojure
ฟังก์ชันไม่จำเป็นต้องรู้สถานะภายนอก และภายนอกก็ไม่จำเป็นต้องรู้ว่าข้างในฟังก์ชันทำอะไร
ต่อให้ไม่รู้สถานะทั้งหมดของโปรแกรม ก็ยังทดสอบหรือดีบักฟังก์ชันใดฟังก์ชันหนึ่งแบบอิสระได้
มันน่าสนใจที่ชุมชน Haskell สุดท้ายก็พยายามประดิษฐ์ mutability ขึ้นมาใหม่ภายใน type system
ประเด็นสำคัญคือการ ควบคุม side effect ด้วยต้นทุนที่ต่ำที่สุด
Haskell มีอุปสรรคในการเริ่มต้นสูงเพราะ type system ส่วน F# ก็ ประนีประนอมเกินไป จนสุดท้ายกลายเป็นเขียนด้วยไวยากรณ์แบบ C#
ด้วย homoiconicity และโครงสร้างข้อมูลอันทรงพลังของ Clojure แนวคิดเรื่อง “ทำงานกับค่า” จึงเป็นสิ่งที่ฉันเข้าใจได้ชัดเจนเป็นครั้งแรก
ถึงจะไม่ใช้ในงานอาชีพ แต่ก็อยากแนะนำอย่างยิ่งสำหรับคนที่ยังไม่มีประสบการณ์กับภาษาฟังก์ชันหรือ Lisp
อยากให้ตัวแปรเป็น immutable โดยพื้นฐาน และทุกอย่างเป็น expression
แต่ในความเป็นจริง ในฐานะนักพัฒนา Clojure ตอนนี้กำลังทุกข์กับ การบุกของ Python
ตอนนี้เลยกำลังทุกข์กับ การบุกของ TypeScript เช่นกัน จึงเข้าใจดี
วิธีนี้มีประโยชน์มากในการ จำกัดขอบเขตของการเปลี่ยนแปลง
ไม่จำเป็นต้องเข้าไปพัวพันกับ สงครามชนเผ่า ระหว่างภาษา
ในยุคที่เน้นการเพิ่มผลิตภาพ เส้นแบ่งเหล่านี้ไม่มีความหมายแล้ว
ขอแนะนำบทความ Don’t Call Yourself a Programmer
แม้จะพยายามลดการ reassign ตัวแปรให้น้อยที่สุด แต่บางครั้งก็ใช้ variable shadowing
ชอบแพตเทิร์นอย่าง
result = result.process()เพราะกระชับดีตัวอย่างเช่น ถ้า
process()เป็น ฟังก์ชันตรวจสอบ ก็อาจทำให้ไม่ชัดเจนว่าค่านั้นผ่านการประมวลผลมา ณ จุดไหนแล้วดังนั้นควรแยกสถานะให้ชัดด้วยชื่อ
ตัวอย่าง:
result = x |> foo |> bar |> bazหรือ(-> x foo bar baz)result.process()นี่มัน result อะไร แล้ว process อะไร?”คนที่มาอ่านโค้ดทีหลังย่อมสับสน
คำว่า “variable” เองก็ทำให้ติดใจมาตลอด
ถ้ามัน immutable แล้วทำไมถึงยังเรียกว่า variable?
ใน Rust จะเปลี่ยนค่าได้ก็ต่อเมื่อต้องระบุ
mutอย่างชัดเจนตรงกันข้าม ใน C ถ้าจะสร้างค่าคงที่กลับต้องพึ่ง preprocessor เลยทำให้สับสน
xรับค่าต่างกันได้ทุกครั้งที่เรียก จึงเป็น ค่าที่แปรผันได้ ในตัวมันเองต่อให้ไม่มีการ reassign ก็ยังเรียกว่า variable ได้
การเขียนโปรแกรมเพียงนำแนวคิดนี้มาใช้ตรง ๆ
ส่วน constant มีค่าเดิมในการรันทุกครั้ง
ดู Variable (mathematics)
ถ้า IDE ช่วย แสดงให้เห็นทางสายตา ว่าตัวแปรไหนถูกเปลี่ยนค่าก็น่าจะดี
เช่น ทำเครื่องหมายเบา ๆ กับตัวแปรที่ถูกแก้ไข
ถ้าใช้
finalให้มากที่สุดเท่าที่ทำได้ โค้ดจะ อ่านง่ายและดูแลง่ายขึ้นIDE ควรเตือน และอนุญาตให้เปลี่ยนค่าได้เฉพาะเมื่อจำเป็นจริง ๆ
เหมือนแนวคิด set vs list ของ Rich Hickey ที่ควรเลือกโครงสร้างที่สื่อความหมายให้ชัด
เคยทำโปรเจกต์หนึ่งที่บังคับใช้ immutability อย่างเข้มงวดเพื่อ thread safety
ผลคือโค้ดอ่านง่ายขึ้น และตามรอยได้ง่ายขึ้นว่าอะไรเปลี่ยนได้บ้าง
ตั้งแต่นั้นมาก็กลายเป็น แฟนพันธุ์แท้ ของ immutability
หลังจากทำงานกับโค้ดเบส Haskell ขนาดใหญ่มา แล้วต้องกลับไปเขียน C อีกครั้ง ก็ยิ่งรู้สึกว่าอยากให้ immutability เป็นค่าพื้นฐาน
แค่
constยังไม่พอถ้าจะเปลี่ยนจริง ๆ ต้องใช้ pointer ส่วน C++ สามารถเปลี่ยนอาร์กิวเมนต์ได้แค่จากการเรียกฟังก์ชัน ทำให้ยิ่งไม่โปร่งใส
เห็นด้วยกับคำพูดที่ว่า “ควรประกาศตัวแปรเกือบทั้งหมดเป็น const”
Rust สมควรถูกพูดถึงในบริบทนี้
ลองจินตนาการถึงไวยากรณ์ที่ให้ immutable เป็นค่าพื้นฐาน และอนุญาต mutable ได้เฉพาะภายในบล็อกหนึ่ง
เช่นเดียวกับบล็อก
withของ Pythonเมื่อออกจากบล็อกแล้วก็กลับไปเป็น immutable อีกครั้ง
แค่ดู borrow checker ของ Rust ก็พอเห็นแล้วว่าแนวคิดนี้ซับซ้อนแค่ไหน
มีคำพูดว่า “State is the enemy”
ยิ่งสถานะมากเท่าไร เงื่อนไขที่ต้องทดสอบก็เพิ่มขึ้นแบบทวีคูณ
immutability คือวิธีหนึ่งในการป้องกัน การระเบิดของสถานะ แบบนี้
Separation of Church and State