• การตัดสินใจด้านการออกแบบหลายอย่างของภาษา Go ถูกทำขึ้นอย่างไม่จำเป็นหรือโดยละเลยประสบการณ์เดิมที่มีอยู่แล้ว
  • ปัญหาเรื่อง การจัดการขอบเขตของตัวแปร error ทำให้การอ่านโค้ดและการตามหาบั๊กทำได้ยากขึ้น
  • มีการออกแบบที่ ไม่เป็นธรรมชาติและไม่สอดคล้องกับการใช้งานจริง ในหลายส่วน เช่น ความเป็นสองแบบของ nil, การใช้หน่วยความจำ, และความสามารถในการพกพาโค้ด
  • ข้อจำกัดของคำสั่ง defer และวิธีจัดการข้อยกเว้นในไลบรารีมาตรฐาน ทำให้การรับประกันความปลอดภัยจากข้อยกเว้นทำได้ยาก
  • การจัดการหน่วยความจำและการรองรับ UTF-8 ที่ไม่เพียงพอ รวมถึงปัญหาสะสมอื่น ๆ กำลังส่งผลเสียต่อคุณภาพของโค้ดเบส Go ในระยะยาว

คำวิจารณ์ระยะยาวต่อภาษา Go

  • ตามที่เคยเขียนไว้ในโพสต์ก่อนหน้า(Why Go is not my favourite language, Go programs are not portable) ฉันได้ชี้ให้เห็น ปัญหาหลายอย่างของภาษา Go มานานกว่า 10 ปี แล้ว
  • โดยเฉพาะอย่างยิ่ง การตัดสินใจออกแบบที่ไม่จำเป็น ซึ่งมองข้ามแนวทางปฏิบัติที่ดีและเป็นที่รู้จักอยู่แล้ว ยิ่งทำให้น่าเสียดายมากขึ้นเรื่อย ๆ

ความไม่เป็นธรรมชาติของขอบเขตตัวแปร error

  • ไวยากรณ์ของ Go ทำให้ ขอบเขตของตัวแปร error (err) กว้างเกินความจำเป็น และเพิ่มโอกาสเกิดความผิดพลาด
    • ในโค้ดตัวอย่าง ตัวแปร err ยังคงอยู่ตลอดทั้งฟังก์ชันและถูกนำกลับมาใช้ซ้ำ ซึ่งทำให้ ความอ่านง่ายและการบำรุงรักษาโค้ด ลดลง
    • แม้แต่นักพัฒนาที่มีประสบการณ์ก็อาจเกิดความเข้าใจผิดและเสียเวลาในการตามหาบั๊กเพราะปัญหาของขอบเขตตัวแปรเช่นนี้
    • ไวยากรณ์ของภาษาไม่เปิดทางให้จำกัดขอบเขตตัวแปรให้แคบลงอย่างเหมาะสม

nil สองรูปแบบ

  • ใน Go มีความสับสนจากการที่ nil ทำงานต่างกันในชนิด interface และชนิด pointer
    • ตามตัวอย่างด้านล่าง แม้ s (pointer) และ i (interface) จะถูกกำหนดเป็น nil แต่ s==i กลับถูกประเมินต่างกัน แสดงถึง พฤติกรรมที่ไม่สอดคล้องกัน
    • นี่เป็นปัญหาประเภทที่โดยทั่วไปแล้วควรหลีกเลี่ยงในการจัดการ null และสะท้อนร่องรอยของการออกแบบที่คิดมาไม่รอบด้านพอ

ข้อจำกัดด้านความสามารถในการพกพาของโค้ด

  • การใช้คอมเมนต์เพื่อทำ conditional compilation ไม่มีประสิทธิภาพอย่างชัดเจนในแง่การบำรุงรักษาและความสามารถในการพกพา
    • หากเคยมีประสบการณ์สร้างซอฟต์แวร์ที่พกพาได้จริง จะรู้ว่าวิธีนี้ทั้งยุ่งยากและก่อให้เกิดข้อผิดพลาดได้ง่าย
    • ประสบการณ์ที่สั่งสมมาในอดีต ทั้งเรื่องความสามารถในการพกพาของโค้ดและกรณีใช้งานจริง ถูกมองข้ามไป
    • รายละเอียดเพิ่มเติมดูได้ที่ Go programs are not portable

ความไม่ชัดเจนของ ownership ใน append

  • ความสัมพันธ์ด้าน ownership ระหว่างฟังก์ชัน append กับ slice ไม่ชัดเจน ทำให้คาดเดาพฤติกรรมของโค้ดได้ยาก
    • จากตัวอย่าง เมื่อฟังก์ชัน foo ทำ append กับ slice ก็ยากที่จะรู้ล่วงหน้าว่าจะส่งผลต่อข้อมูลต้นฉบับอย่างไรจริง ๆ
    • ทำให้ภาษาเต็มไปด้วย “quirk” ที่ต้องจำเพิ่มขึ้น และนำไปสู่ความผิดพลาดได้

การออกแบบคำสั่ง defer ที่ยังไม่ดีพอ

  • Go ไม่ได้รองรับการปลดปล่อยทรัพยากรอย่างชัดเจนแบบหลักการ RAII(Resource Acquisition Is Initialization)
    • เมื่อเทียบกับโครงสร้างจัดการทรัพยากรแบบมีโครงสร้างใน Java และ Python แล้ว ใน Go ไม่ชัดเจนว่าทรัพยากรใดควรถูกปล่อยด้วย defer
    • ดังตัวอย่างการทำงานกับไฟล์ ผู้พัฒนาต้องจัดการปัญหา double-close เอง และลำดับกับวิธีการปล่อยทรัพยากรที่ถูกต้องก็ไม่ชัดเจน

การจัดการข้อยกเว้นในไลบรารีมาตรฐาน

  • แม้ Go จะมีโครงสร้างที่ ไม่รองรับ exception แบบชัดเจน แต่สถานการณ์ผิดปกติอย่าง panic ก็ยังเกิดขึ้นได้
    • ในบางกรณี panic ไม่ได้ทำให้โปรแกรมหยุดทั้งหมด แต่กลับถูกกลืนหายไป
    • มีรูปแบบในไลบรารีมาตรฐาน (fmt.Print, HTTP server ฯลฯ) ที่เพิกเฉยต่อข้อยกเว้น ทำให้ ไม่สามารถรับประกัน exception safety ที่แท้จริงได้
    • สุดท้ายแล้วการเขียนโค้ดให้ปลอดภัยจากข้อยกเว้นยังเป็นสิ่งจำเป็น แต่กลับไม่สามารถใช้ข้อยกเว้นได้โดยตรง

UTF-8 และสตริง

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

ข้อจำกัดของการจัดการหน่วยความจำ

  • ควบคุมการใช้ RAM ได้โดยตรงยาก และความน่าเชื่อถือของ GC(garbage collection) ก็มีข้อจำกัด
    • การใช้หน่วยความจำของ Go เพิ่มขึ้นและเชื่อมโยงไปสู่ปัญหาด้านต้นทุนและประสิทธิภาพในระยะยาว
    • ในสภาพแวดล้อมแบบหลายอินสแตนซ์หรือคอนเทนเนอร์ ปัญหาด้านต้นทุนและการขยายระบบเกิดขึ้นจริง

บทสรุป: มีเส้นทางที่ดีกว่านี้

  • แม้จะมี แนวทางการออกแบบภาษาที่พิสูจน์ประสิทธิภาพมาแล้ว อยู่ก่อน แต่ Go กลับเมินสิ่งเหล่านั้นในหลายด้าน
    • ต่างจากปัญหาในข้อเสนอแรกเริ่มของ Java เพราะในช่วงที่ Go ออกสู่ตลาดนั้น มีแนวทางที่ดีกว่าให้เลือกใช้อยู่แล้ว

เอกสารอ้างอิง

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น