- Zig ปรับโครงสร้างให้ ตรรกะการจัดการแพ็กเกจ ย้ายไปอยู่ในโปรเซส maker ภายนอกตัวคอมไพเลอร์ ให้สอดคล้องกับแนวทางที่แยก
build.zigและระบบบิลด์ออกเป็นคนละโปรเซส - คำสั่ง
zig build,zig fetch,zig init,zig libcถูกย้ายไปฝั่ง maker ทำให้โค้ดสำหรับ HTTP, TLS, Git, การบีบอัด และการจัดการbuild.zig.zonถูกแจกจ่ายในรูปแบบ ซอร์สโค้ด - โครงสร้างนี้ทำให้สามารถแก้ไขความสามารถด้านการจัดการแพ็กเกจได้โดยไม่ต้องคอมไพล์คอมไพเลอร์ใหม่ และ maker ถูกคอมไพล์ด้วย
ReleaseSafeจึงเปิดใช้ การตรวจสอบความปลอดภัย สำหรับงานเครือข่ายด้วย - จุดประสงค์ดั้งเดิมคือการเปิดเผย build server protocol เพื่อแก้ปัญหาการเปลี่ยนแปลง
--build-runnerที่ขัดขวาง ZLS และใน process tree แบบใหม่ maker จะยังคงเป็น parent ของ configurer ทำให้จัดการการรันคอนฟิกซ้ำได้เสถียรกว่าเดิม - แม้ผลกระทบต่อผู้ใช้จะไม่มาก แต่ไฟล์รันไทม์ของ Zig มีขนาดลดลง 4% จาก 14.1MiB เหลือ 13.5MiB และ
--maker-optกับ--zig-lib-dirถูกแทนที่ด้วยตัวแปรสภาพแวดล้อมZIG_DEBUG_MAKERและZIG_LIB_DIRตามลำดับ
การย้ายตรรกะการจัดการแพ็กเกจ
- หลังจากที่ Zig แยกสคริปต์
build.zigของผู้ใช้กับตัวระบบบิลด์ออกเป็นคนละโปรเซสแล้ว ก็ปรับโครงสร้างให้ ตรรกะการจัดการแพ็กเกจ อยู่ฝั่งระบบบิลด์ด้วย - คำสั่งย่อยที่ถูกย้ายไปยังโปรเซส maker มีดังนี้
zig buildzig fetchzig initzig libc
โค้ดที่ถูกถอดออกจากไฟล์รันไทม์ของคอมไพเลอร์
- พื้นที่โค้ดขนาดใหญ่ที่ก่อนหน้านี้อยู่ภายในไฟล์รันไทม์ของคอมไพเลอร์ ตอนนี้ถูกแจกจ่ายในรูปแบบ ซอร์สโค้ด
- ตรรกะการดึงแพ็กเกจ
- HTTP client และระบบเครือข่าย
- TLS และการเข้ารหัสที่เกี่ยวข้อง
- Git protocol
xz,gzip,zstd,flate,zip- การพาร์ส การตรวจสอบความถูกต้อง และการประมวลผลอื่นๆ ของไฟล์
build.zig.zon
- ความสามารถเหล่านี้สามารถแพตช์ได้โดยไม่ต้องบิลด์คอมไพเลอร์ใหม่ ทำให้ผู้ใช้และผู้มีส่วนร่วมทดลองได้ง่ายขึ้น
- ไฟล์รันไทม์ maker ถูกคอมไพล์ในโหมด
ReleaseSafeจึงเปิด การตรวจสอบความปลอดภัย สำหรับงานเครือข่ายของระบบจัดการแพ็กเกจ - โค้ดเข้ารหัสที่ใช้กับเครือข่ายและการแฮชไฟล์สามารถใช้ คำสั่ง CPU แบบพิเศษ ที่มีอยู่บนเครื่องโฮสต์ได้
โครงสร้างโปรเซสและ build server
- แรงจูงใจดั้งเดิมคือการเปิดเผย build server protocol เพื่อแก้ปัญหาที่ขัดขวาง ZLS
- ในโครงสร้างเดิม
zig buildรับหน้าที่ทั้งคอมไพเลอร์และตัวจัดการแพ็กเกจ ส่วน builder ที่อยู่ด้านล่างบรรจุตรรกะbuild.zigของผู้ใช้และการทำงานของระบบบิลด์ - หลังการแยก maker/configurer โครงสร้างจะเป็นดังนี้
zig build: คอมไพเลอร์ + ตัวจัดการแพ็กเกจconfigurer: ตรรกะbuild.zigของผู้ใช้maker: ระบบบิลด์
- ในโครงสร้างนี้
zig build --watchที่ทำงานต่อเนื่องนานๆ ต้องรัน configurer ใหม่เมื่อพบการเปลี่ยนแปลงในbuild.zigหรือไฟล์ที่เกี่ยวข้อง และเพื่อให้เกิดสิ่งนี้ maker ต้องยุติการทำงาน เพื่อเปิดโอกาสให้zig buildรันตรรกะการจัดการแพ็กเกจซ้ำ - ในโครงสร้างใหม่
zig buildรับหน้าที่เฉพาะคอมไพเลอร์ ส่วน maker ที่อยู่ด้านล่างจะรับผิดชอบทั้ง ระบบบิลด์และตัวจัดการแพ็กเกจ และ configurer จะเป็น child process ของ maker - แม้ต้องรันคอนฟิกใหม่ maker ก็ยังคงอยู่ต่อในฐานะ parent process ได้
- สำหรับ build server ที่วางแผนไว้ แทนที่ client จะต้องเชื่อมต่อใหม่หลังเซิร์ฟเวอร์ปิดตัว ก็จะสามารถใช้โครงสร้างที่แจ้ง การเปลี่ยนแปลงการตั้งค่า ไปยัง client ได้
การเปลี่ยนแปลงที่สังเกตได้
- โดยรวมไม่ใช่การเปลี่ยนแปลงที่เข้ากันไม่ได้เป็นส่วนใหญ่ แต่มีความแตกต่างที่ผู้ใช้สังเกตได้
- ขนาดไบนารีของไฟล์รันไทม์ Zig: ลดจาก 14.1MiB เหลือ 13.5MiB บน
ReleaseSmallแบบไม่มี LLVM คิดเป็น ลดลง 4% - แฟล็ก
--maker-optถูกแทนที่ด้วยตัวแปรสภาพแวดล้อมZIG_DEBUG_MAKER - แฟล็ก
--zig-lib-dirถูกแทนที่ด้วยตัวแปรสภาพแวดล้อมZIG_LIB_DIR
- ขนาดไบนารีของไฟล์รันไทม์ Zig: ลดจาก 14.1MiB เหลือ 13.5MiB บน
ประเด็นค้างสำคัญก่อน Zig 0.17.0
- หลังชุดการเปลี่ยนแปลงนี้ ประเด็นค้างสำคัญที่เหลืออยู่จะเป็นงานหลักก่อนติดแท็ก Zig 0.17.0
- ในเดือนกรกฎาคมมีงานประชุมสองงานและการเตรียมงานนำเสนอ จึงคาดว่าในทางปฏิบัติแล้วงานเหล่านี้จะเสร็จได้ช่วงต้นเดือนสิงหาคม
- Techatrix จากทีม ZLS ได้ติดต่อและร่วมมือในงาน build server protocol และกำลัง มองหาสปอนเซอร์
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
นักพัฒนา Zig, Go, Python มักจะประกาศเป็นครั้งคราวว่า เอาน้ำหล่อเย็นออกจากถังเชื้อเพลิงแล้ว และผู้สนับสนุนก็ดีใจกันว่าเป็นผลดีกับภาษาและประสิทธิภาพก็น่าจะดีขึ้นมาก
แต่สิ่งที่น่าสงสัยกว่าคือแต่แรกทำไมถึงเอาน้ำหล่อเย็นใส่ไว้ในถังเชื้อเพลิง
การใส่ระบบจัดการแพ็กเกจไว้ในคอมไพเลอร์นั้นสะดวก แต่เมื่อภาษาและเครื่องมือเริ่มถูกใช้อย่างแพร่หลาย ฟีเจอร์ที่เคยเป็นประโยชน์กับคนทำกลับกลายเป็นหนี้ทางเทคนิคที่ขัดขวางการนำไปใช้
ในหลายโปรเจ็กต์ นักพัฒนามักเลือกทางที่สะดวกเพื่อเลี่ยง “งานยากที่จำเป็นถ้าจะทำให้ถูกต้อง” และเพราะคนอื่นก็ไม่อยากทำงานยากนั้นเหมือนกัน มันเลยกลายเป็นธรรมเนียมไป
ถ้าจะทำตัวจัดการแพ็กเกจใน build system แต่ยังอยากให้มันอยู่ในคอมไพเลอร์ด้วย ก็ต้องกำหนดก่อนว่าจะวาง abstraction อะไรไว้ระหว่างคอมไพเลอร์กับ build system
เรื่องนี้กระทบถึงระบบ import และส่วนพื้นฐานของภาษา ซึ่งเป็นส่วนที่ต้องออกแบบให้ดีและเปลี่ยนทีหลังได้ยาก
ในช่วงแรก การผูก package management กับคอมไพเลอร์แน่น ๆ แล้วออกแบบย้อนกลับจากประสบการณ์ที่ต้องการให้โปรแกรมเมอร์ได้รับ อาจทำให้ภายหลังเปลี่ยน implementation แล้วกระทบผู้ใช้น้อยกว่า
โดยเฉพาะระบบ import ของ Python นั้นดูซับซ้อนมากและมีจุดบกพร่องเยอะ เพราะต้องรองรับการจัดการแพ็กเกจที่เกิดขึ้นได้หลายแบบ
ส่วน Go นั้นออกแบบโดยคำนึงถึง package management มาตั้งแต่แรก ทำให้ไวยากรณ์ที่เกี่ยวกับ import ค่อนข้างเล็กและสะอาด แน่นอนว่าไม่ได้แปลว่าเห็นด้วยกับทุกการตัดสินใจในการออกแบบ
ต่อให้เป็นผลิตภัณฑ์เล็ก ๆ ก็ยังคาดเดาได้ยากว่าองค์ประกอบแต่ละส่วนจะโต้ตอบกันอย่างไร จนต้องแก้ไปเรื่อย ๆ
ลองนึกภาพว่าตอนนี้กำลังสร้าง ภาษาทดแทน C แบบ self-hosting ดู
ยิ่งความเร็วเพิ่มขึ้นมากคนก็ยิ่งเฮกัน แต่ optimization ส่วนใหญ่ไม่ใช่เทคนิคฉลาดล้ำอะไร แค่ใช้ profiler แล้วเจอว่า “เดี๋ยวนะ น้ำหล่อเย็นไม่ควรอยู่ในถังเชื้อเพลิงนี่”
คอขวดด้านประสิทธิภาพ ส่วนใหญ่มักไม่ใช่ข้อจำกัดเชิงแก่นแท้ของฮาร์ดแวร์หรือของปัญหาเอง
มันมีปริมาณงานขั้นต่ำที่ต้องทำเพื่อให้ได้ผลลัพธ์อยู่แล้ว แต่สาเหตุที่แอปพลิเคชันช้ามักแทบจะเป็นเพราะของจุกจิกที่ไปพอกทับเพิ่มจากนั้น
การพัฒนา Zig ให้ความรู้สึกว่าเป็นบรรยากาศที่ ดีต่อสุขภาพมาก
ผมใช้ LLM ทุกวัน และยอมรับว่ามันเก่งจนน่าทึ่งกับปัญหาหลายประเภท แต่ผมไม่ต้องการภาษาการเขียนโปรแกรมที่สร้างโดย LLM
ทุกบรรทัดของโค้ด ทุกการตัดสินใจ และทุก trade-off ในภาษาการเขียนโปรแกรมล้วนสำคัญ
ภาษาที่ถูกออกแบบและเขียนโค้ดจากแค่อารมณ์หรือบรรยากาศจะกลายเป็นหายนะ
ยังไม่เคยมีโมเดลไหนแสดงโค้ดที่ทำให้มั่นใจในทางตรงกันข้ามได้เลย แม้แต่ Fable ที่ดีขึ้นจากโมเดลชั้นนำตัวก่อนอย่างชัดเจนก็ยังเหมือนเดิม
โมเดลไม่ได้มีความต้องการ ไม่มีมุมมองที่มีความหมาย และไม่รู้ว่าความสบายหรือความอึดอัดในภาษานั้นคืออะไร เช่นเดียวกับในอินเทอร์เฟซ GUI หรือ CLI ที่ซับซ้อนพอสมควร
ภาษาที่อย่าง Zig จะไม่มีทางออกมาจาก LLM ได้ เว้นแต่จะก็อปปี้ Zig มา และต่อให้ก็อปปี้ก็น่าจะเป็นสำเนาที่แย่กว่า
คำว่าก็อปปี้ด้วย LLM ในที่นี้ไม่ได้หมายถึงการ
cpsource tree ตรง ๆ แต่หมายถึงให้ LLM ตัวหนึ่งเขียนสเปก แล้วให้อีกตัวนำสเปกนั้นไปสร้างภาษาผมเคยทำบูตโหลดเดอร์และทำงานกับ UEFI ซึ่งโดยส่วนตัวรู้สึกว่าง่ายกว่าทำด้วย C มาก
แต่ก็ยอมรับว่าความสนุกจากการได้เรียนรู้อะไรใหม่ ๆ และดูทันสมัยก็ทำให้มีอคติอยู่พอสมควร
สงสัยว่านี่ใช่การเปลี่ยนแปลงเดียวกับที่ทำให้ Zig เอา
@cImportออกแล้วโยกไปอยู่ใน build system หรือเปล่าถึงจะเข้าใจว่าเป็นเรื่องประสบการณ์ผู้ใช้ล้วน ๆ และการแยก build system ออกจากคอมไพเลอร์เป็นเรื่องสำคัญสำหรับผู้ดูแล แต่ก็ยังรู้สึกเสียดายอยู่นิดหน่อยที่ เสถียรภาพในการพัฒนา มาก่อนประสบการณ์ผู้ใช้
เป็นการตัดสินใจที่ถูกต้อง แต่ก็เศร้านิด ๆ เพราะผมมองว่า
@cImportเป็นฟีเจอร์เด็ดของภาษานี้การเปลี่ยนแปลงนี้เกี่ยวกับโค้ดที่ใช้ดาวน์โหลดและแตกไฟล์แพ็กเกจจากบุคคลที่สาม
การเปลี่ยน
@cImportเป็นส่วนหนึ่งของงานที่ต้องการทำให้ การพึ่งพา LLVM/libclang ของ Zig กลายเป็นตัวเลือกในอนาคต และต่อไปอาจแยกออกเป็นแพ็กเกจภายนอกได้ แต่ดูเหมือนไม่ได้เกี่ยวข้องกันโดยตรงเคยอ่านมาจากที่ไหนสักแห่งว่าเป้าหมายระยะยาวคือย้าย build system เข้าไปอยู่ใน WebAssembly VM
ถ้าใช่ก็น่าทึ่งมาก
เป็นการเปลี่ยนแปลงที่ดีกับ Zig
ผมอยากย้ายจาก Go ไป Zig มากพอสมควร แต่ตอนนี้การยืนดูอยู่ข้าง ๆ ก็สนุกดีเหมือนกัน
ทุกครั้งที่ภาษาใดภาษาหนึ่งสร้างระบบแพ็กเกจของตัวเองขึ้นมา ผมจะรู้สึกแค่ว่าเราพลาดโอกาสใหญ่อะไรบางอย่างไป
ข้อยกเว้นเดียวคือ C/C++ ที่ไม่ว่าจะดีหรือร้ายก็ไม่มีอะไรที่ตั้งหลักได้จริงมากพอ
การเลือกแบบนี้อาจทำให้กระบวนการซับซ้อนมากเมื่อถึงวันที่ต้องผสมหลายภาษาเข้าด้วยกัน
ระบบแพ็กเกจทำให้การทำงานง่ายขึ้น แต่พอถึงจุดที่ต้องใช้ภาษาอื่น มันกลับทำให้ขั้นตอนหลังจากนั้นซับซ้อนกว่าเดิม
คุณต้องการ build system เดียวสำหรับทุกภาษาหรือเปล่า? ระบบอย่าง Bazel ก็มีอยู่และก็มักถูกใช้กับโปรเจ็กต์หลายภาษา แต่ในความเป็นจริงดูเหมือนจะพิสูจน์แล้วว่า build system ที่มีความรู้เฉพาะของแต่ละภาษานั้นจัดการได้ง่ายกว่ามาก
เพราะมันบังคับให้คิดให้รอบคอบก่อนเพิ่ม dependency
dependency แบบนี้มักมี ต้นทุนแฝง อย่างช่องโหว่ด้านความปลอดภัยติดมาด้วย
ระบบสำคัญจำนวนไม่น้อยเขียนด้วย C++ ดังนั้นการไปพึ่งแพ็กเกจภายนอกที่เข้าถึงได้ง่ายเป็นสิบ ๆ ตัว โดยไม่ได้ตรวจสอบแต่ละแพ็กเกจอย่างเหมาะสม จึงมีความเสี่ยงสูงเกินไป
Zig กำลังเข้ามาเติมช่องว่างนั้น
ในทางปฏิบัติก็มีแค่ Buck กับ Bazel แต่ทั้งคู่ก็แบกภาระจากองค์กรขนาดใหญ่ที่อยู่เบื้องหลังมากเกินไป
น่าเสียดาย
การแยกความรับผิดชอบกันทำได้อย่างน่าเชื่อถือมาก