ในโลกของภาษาโปรแกรมมิ่ง กำลังเกิดการเปลี่ยนแปลงอย่างเงียบๆ เกี่ยวกับวิธีที่นักพัฒนาจัดการกับโครงสร้างข้อมูลที่ซับซ้อน การอภิปรายล่าสุดเกี่ยวกับ Accessors.jl ซึ่งเป็นแพ็กเกจของ Julia ที่นำเลนส์มาใช้ ได้จุดประกายการถกเถียงอย่างร้อนแรงเกี่ยวกับความไม่เปลี่ยนสถานะ (immutability) ประสิทธิภาพ และกระบวนทัศน์การเขียนโปรแกรม ขณะที่นักพัฒนากำลังต่อสู้กับงานจัดการข้อมูลที่ซับซ้อนมากขึ้นเรื่อยๆ เลนส์ได้เสนอทางออกที่น่าสนใจซึ่งท้าทายแนวทางแบบเดิมที่เน้นวัตถุ (object-oriented)
อธิบายแนวคิดของเลนส์
เลนส์ให้แนวทางการเขียนโปรแกรมเชิงฟังก์ชันสำหรับการเข้าถึงและปรับเปลี่ยนโครงสร้างข้อมูลแบบซ้อนกัน โดยไม่เปลี่ยนสถานะของวัตถุเดิม แทนที่จะปรับเปลี่ยนฟิลด์โดยตรงเช่น obj.child.foo[3].bar เลนส์จะสร้างสำเนาที่อัปเดตแล้วในขณะที่ยังคงรักษาข้อมูลเดิมไว้ แนวทางที่ให้ความสำคัญกับความไม่เปลี่ยนสถานะนี้มีผลกระทบอย่างมากต่อความน่าเชื่อถือของโค้ด ความปลอดภัยในการทำงานแบบขนาน และความถูกต้องของโปรแกรม แนวคิดนี้อาจดูเป็นนามธรรมในตอนแรก แต่มันแก้ไขปัญหาพื้นฐานในการพัฒนาซอฟต์แวร์สมัยใหม่ที่ความสมบูรณ์ของข้อมูลและพฤติกรรมที่คาดเดาได้เป็นสิ่งสำคัญที่สุด
เลนส์คือ โดเมนเฉพาะภาษาที่ฝังตัว (embedded DSL) สำหรับทำสิ่งนี้ผ่านไวยากรณ์ที่อ่านคล้ายกับรูปแบบที่เปลี่ยนสถานะได้ นอกจากนี้ยังอนุญาตให้รวมการเปลี่ยนแปลงดังกล่าวจำนวนมากเข้าด้วยกัน
พลังของเลนส์จะปรากฏชัดเมื่อต้องจัดการกับการเปลี่ยนแปลงข้อมูลที่ซับซ้อน ไม่เหมือนกับการเชื่อมต่อเมธอดแบบเดิมหรือการเข้าถึงฟิลด์โดยตรง เลนส์สามารถประกอบเข้าด้วยกันได้เหมือนฟังก์ชันทางคณิตศาสตร์ สร้างรูปแบบการเข้าถึงข้อมูลที่นำกลับมาใช้ใหม่ได้ ซึ่งทำงาน across โครงสร้างข้อมูลที่แตกต่างกัน ความสามารถในการประกอบนี้หมายความว่านักพัฒนาสามารถสร้างไปป์ไลน์การจัดการข้อมูลที่ซับซ้อนจากส่วนประกอบที่เรียบง่ายและผ่านการทดสอบมาแล้ว แทนที่จะต้องเขียนลอจิกการเข้าถึงแบบกำหนดเองสำหรับทุกสถานการณ์
กฎของ Lens (คุณสมบัติพื้นฐาน):
- คุณได้สิ่งที่คุณตั้งค่า:
lens(set(obj, lens, val)) == val - การตั้งค่าสิ่งที่มีอยู่แล้วไม่ทำให้เกิดการเปลี่ยนแปลง:
set(obj, lens, lens(obj)) == obj - การตั้งค่าครั้งสุดท้ายเป็นตัวชี้ขาด:
set(set(obj, lens, val1), lens, val2) == set(obj, lens, val2)
การแลกเปลี่ยนระหว่างประสิทธิภาพกับความไม่เปลี่ยนสถานะ
การอภิปรายเกี่ยวกับเลนส์นำไปสู่คำถามเกี่ยวกับประสิทธิภาพอย่างหลีกเลี่ยงไม่ได้ ผู้วิจารณ์แย้งว่าการสร้างสำเนาแทนการปรับเปลี่ยนข้อมูลในที่ต้องช้ากว่าแน่นอน ในขณะที่ผู้สนับสนุนชี้ไปที่การเพิ่มประสิทธิภาพของคอมไพเลอร์และประโยชน์ของโครงสร้างข้อมูลที่ไม่เปลี่ยนสถานะ ในภาษาต่างๆ เช่น Julia ที่เน้นการคำนวณประสิทธิภาพสูง การถกเถียงนี้จึงมีความสำคัญเป็นพิเศษ ความจริงแล้วมีรายละเอียดปลีกย่อยมากกว่าที่การเปรียบเทียบประสิทธิภาพแบบง่ายๆ อาจบอกได้
คอมไพเลอร์สมัยใหม่สามารถเพิ่มประสิทธิภาพการทำงานแบบไม่เปลี่ยนสถานะได้ในวิธีที่น่าประหลาดใจ เมื่อคุณอัปเดตฟิลด์ที่ซ้อนกันลึกโดยใช้เลนส์ มีเพียงส่วนที่ได้รับผลกระทบของโครงสร้างข้อมูลเท่านั้นที่จำเป็นต้องคัดลอก เทคนิคนี้เรียกว่า structural sharing ซึ่งหมายความว่าในขณะที่คุณได้รับประโยชน์ด้านความปลอดภัยของความไม่เปลี่ยนสถานะ ค่าใช้จ่ายด้านประสิทธิภาพไม่จำเป็นต้องเป็นสัดส่วนกับขนาดของโครงสร้างข้อมูลทั้งหมด สำหรับแอปพลิเคชันจำนวนมาก ประโยชน์ด้านความน่าเชื่อถือมีค่ามากกว่าต้นทุนประสิทธิภาพที่ modest โดยเฉพาะเมื่อพิจารณาถึงเวลาดีบักที่ลดลงและสภาวะแข่ง (race conditions) ที่น้อยกว่าในโค้ดที่ทำงานพร้อมกัน
ลักษณะด้านประสิทธิภาพ:
- คัดลอกเฉพาะส่วนที่ได้รับผลกระทบของโครงสร้างข้อมูล (ไม่ใช่การคัดลอกแบบลึก)
- การแชร์โครงสร้างช่วยลดภาระด้านหน่วยความจำให้เหลือน้อยที่สุด
- การเพิ่มประสิทธิภาพของคอมไพเลอร์สามารถกำจัดการคัดลอกบางส่วนในกรณีที่เรียบง่าย
- เป็นประโยชน์สำหรับการเขียนโปรแกรมแบบ concurrent และโครงสร้างข้อมูลแบบ persistent
บริบทของระบบนิเวศ Julia ที่กว้างขึ้น
การอภิปรายเกี่ยวกับเลนส์สะท้อนถึงธีมที่ใหญ่กว่าในชุมชนภาษา Julia โปรแกรมมิ่ง Julia ตำแหน่งตัวเองว่าเป็นผู้แก้ปัญหาสองภาษา (two-language problem) ที่นักพัฒนาเขียน原型ในภาษาที่ช้าและเป็นแบบไดนามิก แต่เขียนส่วนที่สำคัญต่อประสิทธิภาพใหม่ในภาษาที่เร็วกว่าเช่น C++ ด้วยคุณสมบัติเช่นเลนส์ Julia สาธิตความสามารถในการเขียนโปรแกรมเชิงฟังก์ชันในขณะที่ยังคงโฟกัสการคำนวณทางวิทยาศาสตร์ไว้ อย่างไรก็ตาม สิ่งนี้มาพร้อมกับความท้าทายของระบบนิเวศซึ่งสมาชิกในชุมชนอภิปรายอย่างเปิดเผย
นักพัฒนาบางส่วนแสดงความ frustrate กับระบบนิเวศแพ็กเกจของ Julia โดยชี้ให้เห็นว่าเครื่องมือสำคัญเช่น debugger, static arrays และแม้แต่การปรับปรุงคุณภาพชีวิตพื้นฐาน จำเป็นต้องใช้แพ็กเกจเพิ่มเติม ดังที่ผู้แสดงความคิดเห็นหนึ่งคนระบุไว้ คุณต้องการแพ็กเกจเพื่อให้มี debugger ที่ทนได้ คุณต้องการแพ็กเกจเพื่อให้มี static arrays ที่มีประสิทธิภาพ คุณต้องการแพ็กเกจเพื่อให้มี enum ที่คุ้มค่าใช้ แนวทางแบบ modular นี้ทำให้ภาษาหลักมีความเรียบง่าย แต่สร้างแรงเสียดทานสำหรับผู้ใช้ใหม่ที่คาดหวังเครื่องมือแบบรวมเบตเตอรี่มาให้ (batteries-included)
ประเภทของ Lens ทั่วไปใน Accessors.jl:
PropertyLens- เข้าถึง properties/fields ของออบเจ็กต์IndexLens- เข้าถึงสมาชิกของอาร์เรย์ผ่านดัชนี- Composed lenses - สร้างขึ้นโดยใช้มาโคร
@opticหรือตัวดำเนินการ∘
การประยุกต์ใช้จริงที่เกินกว่าทฤษฎี
ในขณะที่เลนส์อาจดูเป็นทฤษฎี แต่พวกมันแก้ปัญหาจริงในโลกแห่งการเปลี่ยนแปลงข้อมูลและการวิวัฒนาการของ schema หลักการเดียวกันที่ทำให้เลนส์มีประโยชน์สำหรับการอัปเดตเรกคอร์ดที่ซ้อนกัน ก็ใช้กับการเปลี่ยนแปลงข้อมูลระหว่างรูปแบบหรือเวอร์ชันที่แตกต่างกันได้เช่นกัน สิ่งนี้ทำให้พวกมันมีค่าอย่างยิ่งในเวิร์กโฟลว์ data science การพัฒนา API และสถานการณ์ใดๆ ก็ตามที่ข้อมูลจำเป็นต้องย้ายระหว่างการแสดงผลที่แตกต่างกันในขณะที่ยังคงความสอดคล้องไว้
ชุมชนการเขียนโปรแกรมเชิงฟังก์ชันได้ยอมรับเลนส์มาเป็นปีแล้ว แต่การยอมรับพวกมันในภาษา mainstream แสดงถึงการเปลี่ยนแปลงในวิธีที่นักพัฒนาคิดเกี่ยวกับการจัดการข้อมูล แทนที่จะถือว่าการเข้าถึงข้อมูลเป็นชุดของคำสั่งเชิงบังคับ (imperative) เลนส์ส่งเสริมให้คิดเกี่ยวกับการเปลี่ยนแปลงข้อมูลเป็นการดำเนินการที่ประกอบเข้าด้วยกันได้ การเปลี่ยนแปลง mental model นี้สามารถนำไปสู่โค้ดที่บำรุงรักษาได้ง่ายขึ้น โดยเฉพาะเมื่อแอปพลิเคชันเติบโตในความซับซ้อนและนักพัฒนาหลายคนทำงานบน codebase เดียวกัน
การอภิปรายที่กำลังดำเนินอยู่เกี่ยวกับ Accessors.jl และเลนส์ใน Julia สะท้อนให้เห็นแนวโน้มที่กว้างขึ้นในการพัฒนาซอฟต์แวร์ไปสู่โค้ดที่เชื่อถือได้ ประกอบได้ และบำรุงรักษาได้มากขึ้น แม้ว่าแนวทางนี้จะต้องเรียนรู้แนวคิดใหม่และอาจเกี่ยวข้องกับการแลกเปลี่ยนด้านประสิทธิภาพ แต่ประโยชน์สำหรับงานจัดการข้อมูลที่ซับซ้อนนั้นน่าสนใจ ขณะที่ภาษาโปรแกรมมิ่งยังคงวิวัฒนาการต่อไอเดียเช่นเลนส์แสดงให้เห็นว่าแนวคิดการเขียนโปรแกรมเชิงฟังก์ชันกำลังหาทางเข้าสู่การปฏิบัติการพัฒนาระดับ mainstream ได้อย่างไร โดยเสนอทางออกใหม่ให้กับปัญหาเก่าของความสมบูรณ์ของข้อมูลและความสามารถในการบำรุงรักษาโค้ด
อ้างอิง: Lenses
