นักพัฒนา Haskell ถกเถียงว่า Newtypes ให้ความปลอดภัยของประเภทข้อมูลจริงหรือเป็นเพียงการป้องกันเชิงสัญลักษณ์

ทีมชุมชน BigGo
นักพัฒนา Haskell ถกเถียงว่า Newtypes ให้ความปลอดภัยของประเภทข้อมูลจริงหรือเป็นเพียงการป้องกันเชิงสัญลักษณ์

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

ความแตกต่างหลักของความปลอดภัย

การอภิปรายมุ่งเน้นไปที่แนวทางความปลอดภัยของประเภทข้อมูลที่แตกต่างกันโดยพื้นฐานสองแบบ: ความปลอดภัยแบบภายในและภายนอก ความปลอดภัยแบบภายในหมายความว่าระบบประเภทข้อมูลเองป้องกันไม่ให้สถานะที่ไม่ถูกต้องถูกแสดง - ไม่มีทางที่จะสร้างค่าที่ผิดกฎหมายได้เลย ในทางตรงกันข้าม ความปลอดภัยแบบภายนอกอาศัยวินัยของโปรแกรมเมอร์และขอบเขตของโมดูลในการรักษาข้อจำกัดต่างๆ

เมื่อนักพัฒนาใช้ algebraic data types ที่มี constructor ชัดเจนเช่น One | Two | Three | Four คอมไพเลอร์สามารถตรวจสอบได้ว่าทุกกรณีได้รับการจัดการแล้ว แต่กับ newtypes ที่ห่อหุ้มประเภทพื้นฐานเช่นจำนวนเต็ม ฟังก์ชันมักจำเป็นต้องมีกรณี catch-all ที่มีข้อผิดพลาดรันไทม์ ซึ่งสร้างจุดล้มเหลวที่อาจเกิดขึ้นซึ่งจะปรากฏขึ้นระหว่างการทำงานมากกว่าการคอมไพล์

หมายเหตุ: Algebraic data types ช่วยให้คุณกำหนดประเภทโดยการรวมประเภทอื่นๆ เช่น การสร้างประเภทที่สามารถเป็นได้เพียง One, Two, Three หรือ Four เท่านั้น

การเปรียบเทียบความปลอดภัยแบบ Intrinsic กับ Extrinsic

แนวทาง กลไกความปลอดภัย การรับประกันเวลา Compile ข้อผิดพลาดเวลา Runtime ภาระการบำรุงรักษา
Intrinsic ( ADTs ) การบังคับใช้ระบบ Type ครอบคลุมกรณีทั้งหมด ถูกกำจัดแล้ว ต่ำ
Extrinsic ( Newtypes ) ขอบเขต Module จำกัด เป็นไปได้ สูง

ปัญหาขอบเขตโมดูล

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

ความท้าทายจะชัดเจนมากขึ้นเมื่อต้องจัดการกับ type class instances หาก newtype จำเป็นต้องใช้งานอินเทอร์เฟซทั่วไปเช่น Monoid นักพัฒนาอาจสร้างเส้นทางที่หลีกเลี่ยงกลไกความปลอดภัยที่ตั้งใจไว้โดยไม่ตั้งใจ สิ่งนี้สร้างภาระการบำรุงรักษาและช่องโหว่ด้านความปลอดภัยที่อาจเกิดขึ้น ซึ่งไม่มีอยู่ในแนวทางที่เป็นภายในอย่างแท้จริง

หมายเหตุ: Monoid เป็นแนวคิดทางคณิตศาสตร์ในการเขียนโปรแกรมที่แสดงถึงประเภทที่มีการดำเนินการแบบเชื่อมโยงและองค์ประกอบเอกลักษณ์ เช่น การบวกกับศูนย์

การประยุกต์ใช้จริงและทางออก

แม้จะมีข้อจำกัดเหล่านี้ ชุมชนยอมรับว่า newtypes มีจุดประสงค์ที่สำคัญในทางปฏิบัตินอกเหนือจากความปลอดภัย มีประโยชน์อย่างยิ่งสำหรับการแก้ไขข้อจำกัดของ Haskell ที่มี type class instance เพียงหนึ่งตัวต่อประเภท ช่วยให้นักพัฒนาสามารถให้การใช้งานหลายแบบสำหรับประเภทพื้นฐานเดียวกัน

นักพัฒนาบางคนโต้แย้งว่าไม่ควรมองข้ามประโยชน์ด้านการตั้งชื่อและเอกสารของ newtypes แม้ว่าจะไม่ให้ความปลอดภัยที่กันกระสุน แต่ก็ทำให้โค้ดอ่านง่ายขึ้นและช่วยป้องกันข้อผิดพลาดง่ายๆ เช่น การสับสน user ID กับ product ID มุมมองนี้ชี้ให้เห็นว่าความปลอดภัยของประเภทข้อมูลที่สมบูรณ์แบบไม่จำเป็นเสมอไปหากทางเลือกให้ประโยชน์ในทางปฏิบัติที่เพียงพอ

การใช้งาน Newtype นอกเหนือจากความปลอดภัย

  • การแก้ไขปัญหา Type Class: การจัดเตรียม instance หลายตัวสำหรับ type พื้นฐานเดียวกัน (เช่น Sum และ Product สำหรับตัวเลข)
  • การสร้างตราสินค้าใน Namespace: การสร้าง type parameter ที่แตกต่างกันสำหรับ generic function
  • การจัดทำเอกสารโค้ด: ทำให้ function signature อธิบายตัวเองได้ชัดเจนยิ่งขึ้น
  • ความปลอดภัยในการ Refactoring: ป้องกันการผสมค่าที่แตกต่างกันทางความหมายแต่มีโครงสร้างเหมือนกันโดยไม่ตั้งใจ

แนวทางทางเลือกและการเปรียบเทียบภาษา

การอภิปรายได้ขยายไปถึงการเปรียบเทียบกับภาษาการเขียนโปรแกรมอื่นๆ นักพัฒนา Rust กล่าวถึงการใช้ newtypes กับการใช้งาน Deref เพื่อให้ได้ความปลอดภัยของประเภทข้อมูลโดยไม่สูญเสียความสะดวกในการใช้งาน ในขณะเดียวกัน ผู้สนับสนุน structural typing โต้แย้งว่าการเน้นไปที่ชื่อมากเกินไปทำให้พลาดภาพรวมใหญ่ของการแปลงข้อมูล

ประโยชน์ที่ใหญ่ที่สุดจากภาษาที่มีการกำหนดประเภทมาจากการใช้เทคนิคพื้นฐานเหล่านี้

สมาชิกในชุมชนบางคนแนะนำว่าทางออกที่แท้จริงอาจเกี่ยวข้องกับการปรับปรุงระดับภาษา เช่น การสนับสนุนที่ดีกว่าสำหรับประเภทที่มีข้อจำกัดหรือระบบโมดูลที่ซับซ้อนมากขึ้นซึ่งสามารถบังคับใช้ข้อจำกัดได้อย่างน่าเชื่อถือมากขึ้น

ปรัชญาระบบประเภทข้อมูลในวงกว้าง

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

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

อ้างอิง: Names are not type safety