Crafting Interpreters: หนังสือ 640 หน้าที่ทำเสร็จใน 15 เดือน
(journal.stuffwithstuff.com)- แม้การเผยแพร่บนเว็บจะจบลงแล้ว Crafting Interpreters ยังต้องใช้เวลาทำงานเพิ่มเติมอีก 15 เดือนกว่าจะกลายเป็นหนังสือจริง และสุดท้ายออกมาเป็นทั้งฉบับพิมพ์, อีบุ๊ก และ PDF
- การเปลี่ยนชุดไฟล์ Markdown และ PNG ให้เป็นหนังสือต้องสร้าง ระบบบิลด์ด้วย Dart, การนำเข้า XML ใน InDesign, การทำอัตโนมัติด้วย JavaScript และการตรวจสอบงานจัดหน้าใหม่ทั้งหมด
- ผลลัพธ์สุดท้ายคือหนังสือขนาด 8×10 นิ้ว 640 หน้า มากกว่า 200,000 คำ มีโค้ดสั้น ๆ 1,133 ชิ้น และภาพประกอบหลายร้อยภาพ จึงมีข้อจำกัดด้านเลย์เอาต์ที่ยุ่งยากกว่าเนื้อหาเว็บทั่วไปมาก
- ตามมาด้วยการทบทวนทั้งเล่ม 5 เดือน, การคัดแก้โดยมืออาชีพ, การจัดหน้า 2 เดือน, การทำดัชนี 2 สัปดาห์, การตรวจปรู๊ฟ และการทำอัตโนมัติเพื่อเปรียบเทียบ PDF
- หนังสือเทคนิคที่จัดพิมพ์เองไม่ได้จบแค่การเขียนเท่านั้น แต่ การทำบิลด์·เลย์เอาต์·การตรวจสอบ·การทำอัตโนมัติสำหรับเผยแพร่ เป็นตัวกำหนดคุณภาพและความสมบูรณ์ของหนังสือ
งานที่ยังเหลือหลังทำเนื้อหาเว็บเสร็จ
- เนื้อหาหลักของ Crafting Interpreters เสร็จแล้วก็จริง แต่ผลลัพธ์ในตอนนั้นยังเป็นรูปแบบที่แปลงไฟล์ Markdown และ PNG เป็นเว็บไซต์ด้วยโค้ด Python
- เป้าหมายตั้งแต่แรกคือหนังสือกระดาษจริง และหลังอัปโหลดบทสุดท้ายขึ้นเว็บ ผู้เขียนพักไปประมาณหนึ่งเดือน
- หลังจากเขียนทุกวันเกือบ 4 ปี ผู้เขียนก็หมดแรงอย่างมาก และสถานการณ์ช่วงต้นปี 2020 ก็เป็นช่วงที่ทำงานต่อได้ยาก
ระบบบิลด์ที่สร้างใหม่ด้วย Dart
- เริ่มจากแก้คำผิดและข้อผิดพลาดที่ผู้อ่านแจ้งผ่าน GitHub issues
- จากนั้นเขียน ระบบบิลด์ ทั้งหมดของหนังสือใหม่ด้วย Dart
- สคริปต์บิลด์ของหนังสือเล่มแรกเป็นสคริปต์ Python ไฟล์เดียวที่เรนเดอร์ไฟล์ Markdown รายบทเป็น HTML และแทรกชิ้นส่วนโค้ดเข้าไป
- Crafting Interpreters ต้องค่อย ๆ ประกอบโค้ดอินเทอร์พรีเตอร์เต็มรูปแบบ 2 ตัวตลอด 30 บท จึงต้องใช้ระบบบิลด์ที่ซับซ้อนกว่า
- ระบบบิลด์ใหม่สามารถพิมพ์โค้ดอินเทอร์พรีเตอร์ออกมาแบบโปรแกรมได้จนถึงบทใดบทหนึ่งหรือจุดใดจุดหนึ่งภายในบท แล้วนำไปคอมไพล์และรันชุดทดสอบอัตโนมัติได้
- เครื่องมือที่ใช้ Python มีภาระการบำรุงรักษาสูงตามระดับความชำนาญของผู้เขียน และยังทำงานช้า
- เวอร์ชัน Dart สร้าง HTML และโค้ดพร้อม syntax highlighting ได้ตรงตามต้องการ และ เร็วกว่า เวอร์ชัน Python เดิม 10 เท่า
- การควบคุมการประมวลผล Markdown ได้มากขึ้นยังมีประโยชน์ภายหลังตอนส่งออก XML สำหรับ InDesign
การออกแบบหนังสือและการตัดสินใจเรื่องขนาดเล่ม
- การออกแบบหนังสือใกล้เคียงกับการสร้าง เฟรมเวิร์ก ขึ้นมาก่อนเหมือนงานพัฒนาเว็บหรือเกม แล้วค่อยเทเนื้อหาลงไปในนั้น
- ใน InDesign ต้องตั้งค่า master สำหรับกำหนดระยะขอบและกริดของหน้า และตั้งค่า style สำหรับฟอนต์·สไตล์·สีของข้อความและวัตถุ
- Crafting Interpreters มีองค์ประกอบหลายอย่างที่ทำให้ออกแบบยาก
- มีข้อความเนื้อหาหลักจำนวนมาก
- มี aside ยาว ๆ จำนวนมากที่อธิบายประโยค โค้ด หรือภาพประกอบเฉพาะจุดซึ่งต้องอยู่ติดกัน
- มีโค้ดจำนวนมาก และข้างโค้ดแต่ละชิ้นมีคำอธิบายตำแหน่งในโปรแกรมผลลัพธ์
- ความกว้างแนวนอนต้องคำนึงถึงบรรทัดโค้ดยาว ๆ พื้นที่ aside และระยะขอบด้านในของหนังสือเล่มหนาไปพร้อมกัน
- ตำรา CS ทั่วไปบนชั้นหนังสือของผู้เขียนมักกว้าง 7.5 นิ้ว แต่ใส่โค้ด aside และระยะขอบได้ยาก จึงตัดสินใจใช้ ความกว้าง 8 นิ้ว
- สำหรับการจัดพิมพ์เอง ต้องใช้ขนาดเล่มที่ KDP และ IngramSpark รองรับอย่างจำกัด และตัวเลือกที่สมเหตุสมผลสำหรับความกว้าง 8 นิ้วคือ 8×10 นิ้ว
- ในแนวตั้ง จัดข้อความให้ตรงกับ baseline grid แบบคลาสสิกขนาด 12pt
XML pipeline สำหรับย้ายเข้า InDesign
- InDesign ไม่เข้าใจ Markdown หรือระบบบิลด์ของผู้เขียนโดยตรง การคัดลอกและวางด้วยมือจึงไม่ใช่ทางเลือกที่เป็นจริงได้
- InDesign รองรับ การนำเข้า XML และการใช้สไตล์อัตโนมัติตามแท็ก
- อย่างไรก็ตาม การรองรับ XML มีข้อจำกัดในการจัดการแท็กซ้อนกัน จึงไม่สามารถจัดการรูปแบบที่ซ้อนแท็กตัวเอียงไว้ในเฮดเดอร์แบบ HTML ได้อย่างถูกต้อง
- เพราะผู้เขียนควบคุมระบบบิลด์เองได้ จึงเขียน custom XML exporter เพื่อสร้างแท็กที่ InDesign รับได้ง่าย
การทำอัตโนมัติด้วย JavaScript ใน InDesign และข้อจำกัด
- การนำเข้า XML จะสร้าง “story” ของ InDesign หรือกระแสข้อความต่อเนื่องหนึ่งชุดที่ไหลตามกล่องข้อความหลัก
- เนื้อหาหลักและโค้ดสั้น ๆ อยู่ในกระแสหลักได้ แต่ aside และ location marker ต้องถูกดึงออกไปไว้ด้านข้าง
- ในหนังสือเล่มก่อน ผู้เขียนตัด aside ออกด้วยมือแล้ววางลงในกล่องข้อความใหม่ แต่หนังสือเล่มนี้มีโค้ดสั้น ๆ 1,133 ชิ้น จึงใช้วิธีเดิมไม่ได้
- InDesign รองรับการเขียนสคริปต์ด้วย JavaScript แต่เอกสารและสภาพแวดล้อมดีบักแย่มาก
- ไม่มีดีบักเกอร์
- ไม่มี stack trace
- ไม่มี debug print แบบทั่วไป
- ใช้ได้แค่
alert()และเมื่อเรียกใช้ สคริปต์จะหยุดทำงาน
- สคริปต์ JavaScript หา aside และ location marker แล้วดึงออกจากกระแสข้อความหลักเพื่อสร้างเป็นกล่องข้อความแยกต่างหาก
- การทำอัตโนมัติสำหรับกำหนดตำแหน่งไม่สำเร็จจนสมบูรณ์
- พยายามจัดวางด้วยฟีเจอร์ anchor ของ InDesign และ Object Style แต่ในบางกรณีทำให้เส้นขอบของโค้ดสั้น ๆ ที่อยู่ใกล้เคียงหายไป
- สุดท้าย location tag บางส่วนยังต้องจัดตำแหน่งด้วยมือ
การแก้ไขต้นฉบับและการคัดแก้
- ทำ รอบการแก้ไข โดยอ่านเนื้อหาทั้งหมดใหม่ตั้งแต่ต้นจนจบ
- แต่ละบทผ่าน draft มาแล้วสามรอบระหว่างการเขียน แต่ผู้เขียนตรวจอีกครั้งเพื่อดูการไหลของหนังสือทั้งเล่ม
- งานนี้ใช้เวลา 5 เดือน และแก้ไขมุกตลกซ้ำ ๆ ส่วนใหญ่
- หลังจากนั้นจ้าง Kari Somerton คัดแก้ในฐานะมืออาชีพ
- เวิร์กโฟลว์การแก้ไขทั่วไปมักใช้ Microsoft Word กับ Track Changes แต่ผู้เขียนต้องการคงเวิร์กโฟลว์ที่ใช้ plaintext และ Git
- Kari Somerton เรียนรู้ Git และระบบบิลด์แบบกำหนดเอง จากนั้นตรวจทั้งเล่มและพบข้อผิดพลาดหลายร้อยจุด
- แม้จะมี draft มาแล้วสี่รอบและมี issues จากผู้อ่านหลายร้อยรายการ คัดแก้มืออาชีพก็ยังพบปัญหาอีกมาก
ข้อจำกัดของการจัดหน้า 640 หน้า
- หลังขัดเกลาถ้อยคำจนพอแล้ว จึงเริ่มจัดหน้ารายบทใน InDesign
- งานแต่ละบทวนซ้ำตามขั้นตอนนี้
- สร้างไฟล์ InDesign ใหม่
- ส่งออก XML
- นำ XML เข้า InDesign
- ใช้ JavaScript ดึง aside และ location marker ออกมา
- ตั้งค่า anchor ให้กับองค์ประกอบ sidebar
- ปรับช่องว่างท้ายหน้า
- ห้าขั้นตอนแรกทำได้ในราว 30 นาทีต่อบท แต่การปรับช่องว่างขั้นสุดท้ายยากที่สุด
- การจัดหน้าหนังสือมี ข้อจำกัดการจัดวางแนวตั้ง หลายอย่าง
- ตัดภาพประกอบกลางหน้าไม่ได้
- aside จะเข้าใจง่ายกว่าหากอยู่ในหน้าเดียวกัน
- โค้ดสั้น ๆ ก็ควรไม่ถูกตัดข้ามหน้าถ้าเป็นไปได้
- ต้องเลี่ยงกรณีที่เหลือเฉพาะหัวข้ออยู่ท้ายหน้า
- ควรเลี่ยง widows and orphans ด้วย
- ในสถานการณ์แบบนี้ InDesign จะดันเนื้อหาไปหน้าถัดไป แต่ทำให้เกิดพื้นที่ว่างสีขาวขนาดใหญ่ที่ด้านล่างของหน้า
- ภาพประกอบและโค้ดสั้น ๆ ทำงานคล้ายปัญหา bin-packing ที่พันกันอยู่ และทำให้การจัดหน้าทั้งเล่มใช้เวลา 2 เดือน
- เพื่อลดช่องว่าง ต้องแบ่งโค้ดสั้น ๆ ออกเป็นสองส่วน ปรับระยะขอบรอบภาพ หรือเปลี่ยนความสูงของภาพประกอบ
ภาพประกอบ ดัชนี และส่วนประกอบหน้า–ท้ายเล่ม
- เลือกใช้ภาพวาดปากกาขาวดำที่เหมาะกับงานพิมพ์ และตอนสแกนครั้งแรกนำเข้าที่ 1200 DPI
- การส่งออกเป็นบิตแมปความละเอียดสูงทำได้ง่าย แต่การวางลงในเลย์เอาต์หน้ากระดาษทำได้ยาก
- เพราะเนื้อหาไม่ได้ใช้รูปแบบ “ดู Figure 123” แต่เป็นโครงสร้างประโยคที่ชี้ตรงไปยังภาพประกอบข้าง ๆ ภาพประกอบจึงต้องอยู่ในตำแหน่งใกล้เคียง
- ไม่ได้จ้างผู้จัดทำดัชนีมืออาชีพ แต่ใช้เวลา 2 สัปดาห์ ไล่อ่านทุกบทอีกครั้งและทำดัชนีเอง
- ฟีเจอร์ดัชนีของ InDesign สามารถเปลี่ยนข้อความที่เลือกเป็นรายการดัชนีและสร้างดัชนีทั้งชุดได้ แต่การเพิ่มรายการเองเป็นงานซ้ำ ๆ และน่าเบื่อ
- ท้ายเล่มมีดัชนี ส่วนหน้าเล่มมีหน้าชื่อเรื่อง หน้าลิขสิทธิ์ คำอุทิศ กิตติกรรมประกาศ และสารบัญที่ InDesign สร้างขึ้น
การออกแบบปก
- ผู้เขียนมองว่าความเป็นศิลปะของปกหนังสือเทคนิคอาจไม่สำคัญเท่านวนิยาย แต่เพราะไม่ได้อยู่ในฐานะศาสตราจารย์ที่บังคับให้ซื้อหนังสือได้ จึงใช้เวลากับปกมาก
- ตอนแรกตั้งใจใช้ภาพถ่ายที่ถ่ายเองเป็นปก แต่หารูปที่เหมาะสมไม่ได้
- สุดท้ายตัดสินใจใช้ ภาพประกอบปากกาและหมึก ซึ่งเป็นภาษาภาพของหนังสือ
- วาดภาพภูเขาที่อธิบายกระบวนการคอมไพล์ใหม่ให้ใหญ่และละเอียดขึ้น และทำตัวอักษรชื่อเรื่องใหม่ให้ดูเหมือนลายมือ
- ชื่อเรื่องใช้ Acumin Pro Extra Condensed พิมพ์ออกมาแล้วลอกด้วยมือเพื่อให้ดูไม่สมบูรณ์ และเลือกพาเลตสีคล้ายคู่มือลูกเสือพิมพ์โรเนียวในยุค 1950
ปรู๊ฟพิมพ์และการตรวจสอบการเปลี่ยนแปลงของ PDF
- อัปโหลด PDF ไปยัง KDP และสั่งปรู๊ฟพิมพ์ หนึ่งสัปดาห์ต่อมาได้รับกล่องหนัก ๆ
- เมื่อเห็นหนังสือจริง ผู้เขียนจึงสัมผัสขนาดของโปรเจกต์ในฐานะวัตถุทางกายภาพ ไม่ใช่แค่ไฟล์ข้อมูล
- เพราะกระบวนการจัดหน้ามีงานมือจำนวนมาก ผู้เขียนจึงอ่านปรู๊ฟพิมพ์เองเพื่อหาข้อผิดพลาดและทำเครื่องหมายด้วย sticky note
- ไฟล์ InDesign ถูกใส่ไว้ใน Git repository แต่เป็นไฟล์ไบนารีทึบขนาดใหญ่ จึงดู diff แบบซอร์สโค้ดไม่ได้
- InDesign อาจเปลี่ยนไฟล์แม้ดูเหมือนไม่มีการแก้ไขจริง ทำให้ยากที่จะตรวจว่ามีอะไรเปลี่ยนไป
- ผู้เขียนเขียน Dart script เพื่อแยกทุกหน้าของ PDF หนังสือออกมาแล้วทำเป็นภาพ PNG แบบไทล์ขนาดใหญ่ภาพเดียว
- ทุกครั้งที่คอมมิต จะส่งออก PDF สร้างภาพไทล์ และใช้ Photoshop action วาดกรอบแดงรอบพิกเซลที่ต่างกันระหว่างสองภาพเพื่อหาหน้าที่เปลี่ยนไป
- วิธีนี้ไม่ได้บอกการเปลี่ยนแปลงโดยละเอียดโดยตรง แต่บอกได้ว่าควรตรวจหน้าใดด้วยสายตา และช่วยยืนยันว่ามีเฉพาะการเปลี่ยนแปลงที่คาดไว้
อีบุ๊กและการเปิดตัว
- หลังแก้ไขปรู๊ฟฉบับพิมพ์เสร็จ จึงทำอีบุ๊ก Kindle และ EPUB ด้วย
- ปรับระบบบิลด์ของตัวเองให้ส่งออก XHTML รุ่นเก่าที่ EPUB ต้องการ รวมถึง metadata และ manifest ได้
- หลังรันคำสั่งบรรทัดคำสั่งไม่กี่รายการ ก็สร้างอีบุ๊ก Kindle และ EPUB ได้ แล้วทดสอบกับ reader หลายตัวพร้อมปรับ CSS
- เมื่อเตรียมไฟล์สุดท้ายเสร็จ ก็อัปเดตหน้าแรกของเว็บไซต์หนังสือให้ลิงก์ไปยังแหล่งซื้อ และจัดการรูปภาพกับเลย์เอาต์ responsive
- หลังอัปโหลดขึ้นสโตร์ อัปเดตเว็บไซต์ และแจ้ง mailing list เสร็จ หนังสือจึงอยู่ในสถานะที่ “เสร็จจริง ๆ”
แผนต่อจากนี้
- แม้จะจบบทสุดท้ายแล้ว ผู้คนก็ยังถามถึงงานถัดไปหรือหัวข้อหนังสือเล่มถัดไป
- หลังทุ่มเทให้โปรเจกต์เดียวเป็นเวลา 6 ปี ผู้เขียนตั้งใจจะยังไม่วางแผนหนังสือใหม่สักพัก
- มีหลายอย่างที่เลื่อนออกไประหว่างการระบาดใหญ่ และมีแผนจะพักโดยยังไม่กำหนดว่าจะทำอะไรต่อ
- ผู้เขียนพูดถึงความเป็นไปได้อย่างการทำเพลง ตกปลา ใช้เวลากับเพื่อนและครอบครัว หรือทำโปรเจกต์ roguelike แต่ไม่ได้ตัดสินใจทันทีว่าจะทำอะไร
- ระบุว่าในสักวันอาจอยากทำโปรเจกต์ใหญ่อีกครั้ง แต่ไม่อยากใช้เวลาอีก 6 ปีกับโปรเจกต์เดียว
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
หน้านี้มีทั้งลิงก์ซื้อหนังสือและลิงก์เวอร์ชันออนไลน์ฟรี: https://craftinginterpreters.com/
หนังสือเล่มนี้คุ้มค่าที่จะซื้อแน่นอน แค่ความใส่ใจที่ Nystrom ทุ่มให้กับ การออกแบบหนังสือฉบับพิมพ์ ก็เพียงพอแล้วสำหรับคนที่ชอบสิ่งพิมพ์ ยิ่งมีภาพประกอบวาดมือและงานเขียนยอดเยี่ยมเสริมเข้าไป ผมว่าเหนือกว่าหนังสือเทคนิค 99% เลย
เป็นหนึ่งในหนังสือเทคนิคที่ดีที่สุดเท่าที่เคยอ่านมา โครงสร้างที่แต่ละบทมี โค้ดที่ค่อยๆ พัฒนาไปทีละขั้น และเหลือโปรแกรมที่รันได้ไว้นั้นเป็นแนวคิดที่น่าทึ่ง และผมชื่นชมผู้เขียนที่ทำมันได้จริง
ก่อนหน้านี้ผมเคยเขียนอะไรคล้ายๆ กันมาก่อน เลยอ่านผ่านๆ ส่วน tree-walk interpreter แต่ได้เรียนรู้มากขึ้นมากจากการทำตาม bytecode interpreter ที่ใช้ C
ตอนแรกนึกว่าบทความนี้เป็นของใหม่ เลยคิดว่ามีฉบับพิมพ์ครั้งที่สองออกมาแล้ว
อยากแสดงความยินดีกับผู้เขียน หนังสือเล่มนี้เป็นแหล่งข้อมูลที่ยอดเยี่ยม ไม่ใช่แค่มีความลึกทางเทคนิคในด้านภาษาเท่านั้น แต่ยังมีรายละเอียดเล็กๆ ในเลย์เอาต์และกราฟิกที่ช่วยดึงให้ผู้อ่านอ่านต่อไปได้ รู้สึกเหมือนเป็น หนังสือที่จะยังมีคุณค่าไปอีกนาน
ผมเริ่มตามอ่าน Crafting Interpreters ตั้งแต่ปี 2017 และระหว่างทำครึ่งแรกของหนังสือ ผมเขียน implementation ของ lox ด้วย Scala แทน Java ซึ่งทำให้ tokenizer/lexer/parser/interpreter ไม่ใช่เรื่องลึกลับอีกต่อไป
ก่อนหน้านั้นผมเคยคิดว่ามันเป็นดินแดนลึกลับที่มีแค่โปรแกรมเมอร์บางกลุ่มเท่านั้นที่แตะต้องได้ แต่ผมว่าทั้งหมดเป็นเพราะงานเขียนที่ยอดเยี่ยมของ Nystrom และความเข้าใจลึกซึ้งในหัวข้อ เขาเริ่มเขียน interpreter ตัวที่สองด้วย Rust แต่ชีวิตยุ่งจนทำไปได้ไม่มากและไม่จบ ตอนนี้คงถึงเวลากลับไปทำต่อแล้ว บทความนี้เป็นบทความเมื่อหลายปีก่อน แต่ผมไม่รู้ว่ามีฉบับหนังสือจริงออกมาแล้ว ถึงจะไม่ใช่วิธีเรียนที่เหมาะกับผมที่สุด แต่ก็อยากซื้อสักเล่มไว้สะสมและสนับสนุนผู้เขียน
เป็นหนึ่งในหนังสือเทคนิคคอมพิวเตอร์ระดับดีที่สุดเท่าที่เคยอ่านมา อ่านสนุกมากจริงๆ และได้เรียนรู้เยอะมาก
ไม่ใช่แค่เนื้อหาทางเทคนิคที่ยอดเยี่ยม แต่งานเขียนก็ดี สนุก และภาพประกอบก็ดีด้วย ผมมองว่าเป็น ความสำเร็จระดับหมุดหมาย
มีบทสัมภาษณ์ยอดเยี่ยมที่ Bob พูดถึงหนังสือเล่มนี้ น่าฟังมาก: https://corecursive.com/032-bob-nystrom-on-building-an-inter...
น่าขันตรงที่ผมใช้เวลา 15 เดือนกว่าจะอ่านและทำหนังสือเล่มนี้จนจบ :). เข้าใจสถานการณ์ของผู้เขียนมาก หนังสือยอดเยี่ยมจากผู้เขียนที่ขยันและเก่งมาก และผมผูกพันกับหนังสือเล่มนี้จนถึงขั้นทำเพจจากสิ่งที่ได้เรียนรู้: https://hexmos.com/compiler
ผู้เขียนเปลี่ยนจาก กราฟิกดีไซเนอร์มาเป็นวิศวกรคอมไพเลอร์ งั้นหรือ? น่าทึ่งและน่าประทับใจมาก
หนังสือเล่มนี้อยู่บนชั้นหนังสือของผมแล้ว มันจะเป็นหนังสือเรื่อง interpreter เล่มถัดไปที่ผมอ่านต่อจาก “Writing an interpreter in Go” และเล่มนั้นมีประมาณ 200 หน้า ซึ่งถูกใจผมมากจริงๆ
เพิ่งทำ lexer ด้วย Rust แทน Java เสร็จ และตั้งตารอส่วนที่เหลือ ตอนนี้ถือว่าเป็นหนังสือที่ยอดเยี่ยม