3 คะแนน โดย GN⁺ 2023-07-28 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • จากกระแสที่กำลังคิดถึงภาษาที่จะยังใช้ได้ในอนาคต ตัวเลือกครั้งนี้ใกล้เคียงกับการทดลองด้วยตัวเองกับ เวิร์กโฟลว์การพัฒนาที่เก่าแก่แต่ยังทันสมัย มากกว่าความคุ้มค่าเชิงปฏิบัติ
  • Janet เป็นภาษาสคริปต์ขนาดเล็กสาย Lisp/Clojure ที่มุ่งไปยังพื้นที่คล้ายกับ Lua และดึงดูดความสนใจด้วยไลบรารีมาตรฐานและ เมตาโปรแกรมมิงบนพื้นฐานของมาโคร
  • จุดเด่นของ Janet ที่ดูน่าสนใจอย่างความพกพาได้ ความสามารถในการฝังในโปรแกรมอื่น และ Parsing Expression Grammars นั้นสอดคล้องกับความต้องการปัจจุบันค่อนข้างจำกัด ขณะที่ตัวเลือกเดิมอย่าง CHICKEN Scheme, CLISP, SBCL และ Packrat ก็ดูเพียงพอแล้ว
  • แก่นสำคัญของ การพัฒนาที่ขับเคลื่อนด้วย REPL ใน Common Lisp คือการสามารถตรวจดูและแก้ไขสแต็ก ตัวแปร และข้อมูลสดในสถานะที่เกิดข้อผิดพลาดระหว่างรันไทม์ แล้วกลับไปรันงานที่หยุดค้างต่อได้
  • แม้จะมีต้นทุนในการเรียนภาษาใหม่และไลบรารีมาตรฐาน แต่ผู้เขียนเห็นว่าคุ้มค่าที่จะลองสัมผัสโดยตรงในสภาพแวดล้อมที่เวิร์กโฟลว์นี้ถูกออกแบบไว้ตั้งแต่ต้น

เหตุผลที่ความสนใจย้ายจาก Janet ไปยัง Common Lisp

  • เคยครุ่นคิดถึง future-proof programming languages เพราะอยากเขียนโค้ดที่ยังใช้ได้ในอนาคต แต่เมื่อจะสร้างและแชร์อะไรบางอย่าง ก็ยึดท่าทีแบบปฏิบัตินิยม โดยเลือกภาษาที่ได้รับความนิยม พกพาได้ และใช้งานสะดวก
  • ขณะเดียวกันก็สำรวจภาษาโปรแกรมและเครื่องมืออื่น ๆ เพื่อความสนุกและการทดลองด้วย
    • เคยจับตาภาษาที่น่าสนใจชื่อสั้น ๆ อย่าง Nim และ Zig
    • การทดลองแบบนี้บางครั้งทำให้ค้นพบวิธีเขียนโปรแกรมใหม่ ๆ หรือสิ่งที่ภายหลังกลายเป็นเครื่องมือจำเป็น
  • ช่วงหลังได้อ่านหนังสือฟรี Janet for Mortals จึงเริ่มสนใจ Janet
    • Janet เป็นภาษาสคริปต์ที่ได้รับแรงบันดาลใจจาก Lisp/Clojure ขนาดค่อนข้างเล็ก ซึ่งมุ่งไปยังช่องว่างคล้ายกับ Lua
    • มีไลบรารีมาตรฐานจริง ๆ เมตาโปรแกรมมิงแบบ Lisp และการรันในช่วงคอมไพล์ผ่านมาโคร
  • สิ่งที่ดึงดูดที่สุดใน Janet นอกจากความเป็น Lisp โดยทั่วไปคือ ความพกพาได้ ความสามารถในการฝังในโปรแกรมอื่น และ Parsing Expression Grammars
    • ตอนนี้ยังไม่มีความจำเป็นต้องฝังภาษาไว้ในโปรแกรมอื่น ความสามารถในการฝังจึงไม่ได้เป็นเหตุผลชี้ขาด
    • ด้านความพกพา CHICKEN Scheme, CLISP และ Steel Bank Common Lisp ก็ดูใช้ได้ดีเช่นกัน
    • ส่วนการพาร์ส Packrat ก็ดูเป็นตัวเลือกที่สมเหตุสมผล
  • สุดท้ายแล้ว เหตุผลที่ต้องเลือก Janet แทน Lisp เดิม ๆ จึงลดลง

เวิร์กโฟลว์ที่ขับเคลื่อนด้วย REPL ซึ่งทำให้เริ่มเรียน Common Lisp

  • บทความ ที่พูดถึง เวิร์กโฟลว์ที่ขับเคลื่อนด้วย REPL ซึ่งแทบจะเป็นเอกลักษณ์ของ Common Lisp กลายเป็นแรงจูงใจในการเรียน
  • สถานการณ์ตัวอย่างคือฟังก์ชัน foo เรียกฟังก์ชัน bar ที่ยังไม่ได้ถูกนิยาม และเกิดข้อผิดพลาดระหว่างการรัน foo
    • ในสภาพแวดล้อม Lisp รุ่นเก่าหรือ Smalltalk การหยุดของ foo จะนำไปสู่ breakloop
    • breakloop คือ REPL เต็มรูปแบบที่อยู่ภายในสภาพแวดล้อมแบบไดนามิกของฟังก์ชันที่ถูกหยุดไว้
    • สามารถเลื่อนไปมาระหว่าง call stack ที่หยุดค้าง และตรวจดูตัวแปรที่มองเห็นได้ในแต่ละ stack frame
    • สามารถตรวจสอบและแก้ไขข้อมูลสดของโปรแกรมที่กำลังรันอยู่ได้
    • หากสาเหตุคือค่าตัวแปรหรือฟิลด์ที่ผิด ก็เปลี่ยนค่าแล้วกลับไปรันฟังก์ชันที่หยุดไว้ต่อได้
    • สามารถนิยามฟังก์ชัน bar ที่ขาดไปภายใน REPL แล้วกลับไปรัน foo ต่อเพื่อให้ได้ผลลัพธ์ที่สมเหตุสมผล
  • แม้เคยเห็นฟีเจอร์ “edit and continue” มาหลายแบบแล้ว แต่ประเด็นที่ Common Lisp ถูกสร้างขึ้นโดยคำนึงถึงเวิร์กโฟลว์ลักษณะนี้ทำให้รู้สึกใหม่
  • ประสบการณ์ดีบักที่ผ่านมาเปลี่ยนจาก printf debugging ไปสู่การใช้ดีบักเกอร์จริง
    • printf debugging คือการใส่โค้ดบันทึกล็อกชั่วคราว คอมไพล์ใหม่ แล้วตรวจดูผลลัพธ์ที่รันได้
    • ในสถานการณ์ที่แก้ไขหรือรันโปรแกรมใหม่ไม่ได้ ก็จำเป็นต้องเรียนรู้ดีบักเกอร์
    • ดีบักเกอร์จริงเป็นเครื่องมือที่ถูกต้อง แต่การตั้งค่าสภาพแวดล้อมสำหรับดีบักอาจเจ็บปวด หรือบางครั้งอาจทำไม่ได้เลย
  • Common Lisp ดูเหมือนเป็นสภาพแวดล้อมที่ผลักการดีบักไปอีกขั้น
    • เคยมีประสบการณ์แก้หน่วยความจำในดีบักเกอร์เพื่อทดสอบแนวทางแก้ไขที่เป็นไปได้
    • แต่สิ่งที่ดึงดูดเป็นพิเศษคือการเขียนโค้ดใหม่และแพตช์โค้ดได้ภายในโปรเซสที่กำลังรันอยู่
  • เป้าหมายของการเรียน Common Lisp คือการตรวจสอบด้วยตัวเองว่าวิธีนี้เป็นรูปแบบการทำงานที่สนุกหรือไม่ ขณะ สร้างโปรแกรมแบบโต้ตอบ
  • ยังไม่แน่ใจว่าการเรียนภาษาใหม่และไลบรารีมาตรฐานใหม่เพื่อสำรวจเวิร์กโฟลว์ใหม่เป็นทางเลือกที่ดีที่สุดหรือไม่ แต่ก็ยังไม่เห็นทางเลือกที่ดีกว่าอย่างชัดเจน
    • ภาษาอื่นอาจมีการผสาน REPL กับเอดิเตอร์ในลักษณะคล้ายกัน
    • แต่ผู้เขียนมองว่าฟีเจอร์เหล่านั้นไม่ได้ถูกสนับสนุนมาตั้งแต่ต้น หากแต่ถูกเติมเข้ามาภายหลัง จึงอาจมีบั๊กมากกว่า
    • แม้สุดท้ายจะไม่พอใจ อย่างน้อยก็ยังถือว่าได้ลองภายใต้เงื่อนไขที่ดีที่สุดด้วยเครื่องมือมาตรฐานแล้ว
  • แหล่งข้อมูลที่ช่วยในการเรียน Common Lisp ถูกรวบรวมไว้ในบทความแยก Common Lisp resources

1 ความคิดเห็น

 
GN⁺ 2023-07-28
ความคิดเห็นจาก Hacker News
  • ไม่คิดว่าจะได้เห็นบทความของตัวเองที่นี่ สักวันหนึ่งก็อยากเขียนบทความต่อเนื่องเหมือนกัน แต่ตอนนี้ยังเป็นมือใหม่อยู่
    สิ่งที่ชอบใน Common Lisp จนถึงตอนนี้คือ ระบบ condition ที่เรียบร้อย และสามารถควบคุมโค้ดที่อยู่ไกลออกไปได้ง่ายด้วย restart ส่วน การพัฒนาแบบขับเคลื่อนด้วย REPL ก็สะดวกในสถานการณ์ที่ยังไม่ค่อยรู้ว่าจะเกิดอะไรขึ้น เช่น ตอนพาร์สแหล่งข้อมูลที่ไม่คุ้นเคย เพราะสามารถแก้โค้ดแล้วทำต่อได้โดยไม่เสียบริบท
    มี implementation หลายตัวและทำงานร่วมกันได้ดีด้วย ในกรณีหนึ่งจึงสามารถสลับใช้ SBCL เพื่อความเร็ว กับ CLISP เพื่อลดการใช้หน่วยความจำได้ หนึ่งในเหตุผลที่ทำให้เอียงมาทาง CL มากกว่า Scheme ตอนเรียน Lisp ก็คือมี implementation ที่เข้ากันได้หลายตัว
    แม้จะเป็นมือใหม่ Emacs แต่การผสานกับ Common Lisp ก็ยอดเยี่ยม และยังทำงานได้ดีบนเน็ตบุ๊กที่ช้ามากซึ่งใช้พัฒนาอยู่ ทุกวันนี้ในยุคที่มีคอมพิวเตอร์เร็ว ๆ, VS Code และ language server ข้อดีนี้อาจไม่ใหญ่เท่าเดิม แต่ให้ความรู้สึก retrofuture ชัดเจน
    มีจุดที่น่าเสียดายอยู่เหมือนกัน ตัวจัดการแพ็กเกจที่นิยมที่สุดอย่าง QuickLisp ก็ใช้ได้ แต่ยังไม่ถึงระดับฟีเจอร์ที่คุ้นเคยใน ecosystem ภาษาใหม่ ๆ ตัวภาษาเองเหมือนถูกตรึงไว้ในกาลเวลา ทำให้หลายอย่างอย่าง thread, synchronization, argument บรรทัดคำสั่ง ฯลฯ ต้องพึ่งไลบรารีสำหรับการทำงานร่วมกัน และถ้า SBCL รองรับ การ build แบบ static ทั้งหมด เพื่อให้แจกจ่าย binary ไปยัง Linux distribution ที่ไม่ใช่ glibc ได้ ก็คงดีมากจริง ๆ
    • ก็มีข้อเสียอยู่หลายอย่างเหมือนกัน ครั้งสุดท้ายที่ตรวจสอบ QuickLisp ดึงแพ็กเกจผ่าน HTTP แบบข้อความล้วนเท่านั้น ไม่มีทั้งการเข้ารหัส และไม่มีกลไกตรวจสอบเพื่อจับการแก้ไขระหว่างส่ง
      ถ้างานสำคัญอย่างการดึงซอร์สโค้ดไม่รองรับการเข้ารหัสหรือการยืนยันตัวตน ผมมองว่า QL ก็ยากจะเป็นตัวเลือกสำหรับคนที่ใส่ใจด้านความปลอดภัย
      อีกเรื่องคือ SBCL ถูกโฮสต์บน SourceForge ก็เป็นปัญหา SourceForge เคยฝังมัลแวร์ลงใน archive ดาวน์โหลดในอดีต จึงมองว่านี่ก็เป็นประเด็นความปลอดภัย ทุกวันนี้ดูเหมือนไม่มีเหตุผลสมควรให้ต้องใช้ SourceForge ต่อไป และก็ไม่ค่อยเข้าใจว่าทำไมโปรเจกต์สำคัญ ๆ ยังอยู่ที่นั่น
      ผมชอบ Lisp และโดยเฉพาะ Common Lisp มากจริง ๆ แต่สิ่งเหล่านี้ทำให้ถอยห่างออกมา และดูเหมือนไม่ค่อยมีความสนใจจะแก้ไขด้วย
    • ถ้าต้องการ static build และใช้ SBCL ที่เก่ากว่านิดหน่อยได้ งานของ daewok ที่ build และ link SBCL ในสภาพแวดล้อม musl อาจเป็นทางออกที่มองหาอยู่ เคยลองย้ายแพตช์ไปยังเวอร์ชันล่าสุดแล้ว แต่เกิด segmentation fault เพราะการเปลี่ยนแปลง upstream
      https://www.timmons.dev/posts/static-executables-with-sbcl.h...
      https://www.timmons.dev/posts/static-executables-with-sbcl-v...
    • จะให้ cons หนึ่งตัว
      https://cons.io
      Gerbil/Gambit Scheme เป็นทางเลือกแทน CL ที่สามารถสร้าง binary แบบ static ทั้งหมดได้
    • กำลังสร้าง CIEL สำหรับเวลาที่อยากเริ่มด้วยชุดไลบรารีดี ๆ (json, csv, database, HTTP client, argument บรรทัดคำสั่ง, language extension ฯลฯ): https://github.com/ciel-lang/CIEL/
      ใช้ได้ทั้งเป็นไลบรารี Quicklisp ปกติ, เป็น core image ที่เริ่มได้ทันที หรือเป็น binary ก็ได้ สคริปต์ก็รันได้แทบจะทันที จึงมีบางส่วนคล้าย Babashka ตอนนี้ยังอยู่ระหว่างเก็บรายละเอียด และยังไม่ใช่ v1.0
      ในบล็อก เรื่อง “จัดการ runtime error โดยแก้โค้ดที่พังอยู่กับที่โดยไม่ต้อง restart” ควรจะเอาตัวอย่างนี้มาแสดง: https://www.youtube.com/watch?v=jBBS4FeY7XM
      เมื่อรันการคำนวณที่ยาวและหนัก แล้วเกิด error ในขั้นตอนสุดท้าย แทนที่จะรันใหม่ตั้งแต่ต้น ก็เข้า interactive debugger ไปที่บรรทัดที่ผิด compile ฟังก์ชันที่แก้แล้ว จากนั้นกลับไปที่ debugger และ resume การทำงานจากจุดหนึ่งใน stack frame แล้วจะเห็นว่าโปรแกรมผ่านไปได้
  • ที่ทำงานใช้ Clojure แต่ทุกครั้งที่ต้อง debug หรือต้องการโค้ดที่เร็ว ก็คิดถึงแทบทุกอย่างของ Common Lisp การกดดูส่วนใดก็ได้ของ stack ภายใน error ที่ซ้อนกันเพื่อดู lexical binding มีประโยชน์มาก และวิธีคลิก object แล้วคัดลอกเข้า REPL ด้วย M- รู้สึกว่าดีกว่า tap> ที่ Clojure มีให้มาก แม้จะใช้เครื่องมืออย่าง Portal ก็ยังมองว่า tap> ใกล้เคียงกับ pretty-print ที่แต่งหน้ามาแล้วมากกว่า
    ด้านประสิทธิภาพ Common Lisp ก็สามารถระบุ static type ได้ และ SBCL จะสร้างโค้ดที่มีประสิทธิภาพมากเมื่อทำแบบนั้น การรัน DISASSEMBLE กับโค้ดที่เขียนเองเพื่อดูว่าจริง ๆ แล้วปล่อยอะไรออกมาและ optimize นั้นมีประโยชน์ โดยเฉพาะแพ็กเกจอย่าง SB-SIMD และ Loopus ช่วยงานแอปพลิเคชันคำนวณเชิงตัวเลขได้มากจริง ๆ
    • สรุปความไม่พอใจที่รู้สึกตอนเขียนวิทยานิพนธ์ปริญญาโทด้วย Clojure ได้ดี ไม่ใช่ว่าไม่พอใจกับตัวเลือกนี้เอง Clojure มีความรู้สึกแบบ “ทุกอย่างคือ map ของ key-value” และแนวทาง “ถ้า key :quack เป็น true ก็ปฏิบัติกับมันเหมือนเป็ด” ที่ผสมกันได้ดี จึงเหมาะกับการออกแบบเกมแบบ entity-component-system
      แต่ตลอด 1 ปีที่ผ่านมา คิดถึงวิธีพัฒนาของ Common Lisp และ ระบบ condition มากจริง ๆ ทั้งที่ก็ไม่ได้เป็นแฮ็กเกอร์ CL ที่เชี่ยวชาญขนาดนั้น แต่ก็ซึมซับได้เร็วมาก ถ้า CLOS กับชนิดข้อมูล primitive ของ CL เข้ากันได้ดีกว่าที่เห็นก็คงดี
    • ใน Clojure น่าลองดู flowstorm สามารถ step เดินหน้า-ถอยหลังภายในฟังก์ชันได้ และส่ง map ไปยัง REPL พร้อมกับฟังก์ชันเหล่านั้นได้
    • ในสองภาษานี้ ไม่มีภาษาไหนที่ตัวภาษาเองมีฟีเจอร์ส่ง form ไปยัง REPL ผมมองว่านั่นเป็นฟีเจอร์ของเครื่องมือ
      ใน Cider ทำได้ง่ายแน่นอน และเครื่องมือหลัก ๆ ของ editor อื่นก็น่าจะทำได้ดีพอ ๆ กัน
    • ใน Clojure ก็ทำสิ่งที่คล้ายกับ DISASSEMBLE ได้ในระดับหนึ่ง

มีโปรเจกต์เสริมอย่าง https://github.com/Bronsa/tools.decompiler และยังมี JitWatch ของ OpenJDK (https://github.com/AdoptOpenJDK/jitwatch) ด้วย JVM อื่น ๆ ก็มีเครื่องมือคล้ายกัน
แม้จะไม่ตรงไปตรงมาเท่า Lisp แต่ก็ทำได้อยู่ดี

  • Steel Bank Common Lisp คือม้าทำงานที่ช่วยให้สร้างบริษัทซอฟต์แวร์ที่ทำกำไรได้ ถ้าไม่มีสิ่งนี้คงไม่สามารถทำงานได้มีประสิทธิภาพขนาดนี้ เวิร์กโฟลว์ที่ขับเคลื่อนด้วย REPL นั้นยอดเยี่ยม อิมเมจของ Lisp ก็แข็งแกร่งมาก และประสิทธิภาพก็สูง
    • สำหรับคนที่สงสัย แชร์ได้ไหมว่าบริษัทเหล่านั้นคือบริษัทไหน?
    • ดูเจ๋งดี แต่ตอนนี้ขี้เกียจเกินกว่าจะกลับไปใช้ Emacs อีก ส่วนใหญ่ใช้ VSCode ที่แทบจะเป็นค่าเริ่มต้นสำหรับการพัฒนา Python และ JavaScript อยู่แล้ว เพราะอยู่ในบทบาท CTO เลยไม่ได้เขียนโค้ดเต็มเวลา
    • ผมคิดว่ายุคที่สแตกเทคโนโลยียังมีความสำคัญอยู่บ้างนั้นส่วนใหญ่ผ่านไปแล้ว
      การสร้างอะไรสักอย่างที่ลึกลับแต่ทำงานได้ดีอย่างน่าดึงดูดนั้นก็ถือว่าดี แต่ตอนนี้คงไม่มีใครอยากไปแตะมันแล้ว นอกจากคนที่บรรยายเงินเดือนตัวเองด้วยจำนวนเลขศูนย์ได้
    • เห็นบอกว่าเวิร์กโฟลว์ที่ขับเคลื่อนด้วย REPL นั้นน่าทึ่ง แต่สงสัยว่าไม่รู้กันหรือไงว่าทุกวันนี้ภาษาแบบ virtual machine/interpreter แทบทั้งหมดมี REPL กันแล้ว
      https://www.digitalocean.com/community/tutorials/java-repl-j...
      https://github.com/waf/CSharpRepl
      https://pub.dev/packages/interactive
      ยังไม่ต้องพูดถึง Ruby, Python, PHP, Lua แม้แต่ C++ ก็มี REPL แบบกระท่อนกระแท่น: https://github.com/root-project/cling
      ทุกครั้งที่บอกว่า REPL ไม่ใช่จุดแตกต่างของฝั่ง Lisp อีกต่อไป ก็โดน downvote
  • ถ้าสนใจ กราฟิก 3D มี codebase ของ Common Lisp ที่น่าลองดู ผมพยายามคงให้มันเรียบง่ายและเข้าใจง่าย
    https://github.com/kaveh808/kons-9
    • ลองหา วิดีโอสอน CL ของ Kaveh บน YouTube ดูก็ดี ทำออกมาดีจริง ๆ
  • ในฐานะนักพัฒนา Clojure break loop และเวิร์กโฟลว์ที่ขับเคลื่อนด้วย REPL ดูเจ๋ง และน่าจะช่วย Clojure ได้แน่นอน มันอาจทำให้ใกล้เคียงกับการเขียนโค้ดฟรอนต์เอนด์ JS/TypeScript ที่ใช้เครื่องมือดีบักยอดเยี่ยมของเบราว์เซอร์มากขึ้น น่าเสียดายที่สถานะของเครื่องมือและการสนับสนุนจากชุมชนใน ecosystem ของ Clojure ตอนนี้ดูค่อนข้างอ่อนแอ
    • Clojure เองก็เลียนแบบโฟลว์ REPL จริง ๆ ได้ในระดับหนึ่ง ถ้าเป็นกรณีที่มีการเรียกใช้ลึกในลำดับชั้นของโค้ดต่อทุก request เช่นเว็บเซิร์ฟเวอร์ ระหว่างที่เซิร์ฟเวอร์ยังรันอยู่ ก็เขียนและโหลดฟังก์ชันหลายตัวใหม่ แล้วส่ง request อีกครั้งจากเบราว์เซอร์ได้
      เพียงแต่การนิยามใหม่และเขียนทับแบบนี้สุดท้ายจะทำให้ namespace สกปรก และเมื่อถึงจุดหนึ่งบางอย่างก็พังจนต้องโหลดเซิร์ฟเวอร์ใหม่
  • ชอบ CL และคิดถึงมันมากเวลาไปทำอย่างอื่น เมื่อเทียบกับประสบการณ์ใช้ Lisp เมื่อหลายสิบปีก่อน หลายอย่างในภาษา “สมัยใหม่” ช่างเจ็บปวดจนขำไม่ออก
    มันมีข้อเสียเยอะเหมือนกัน แต่ก็ไม่ได้แย่ไปกว่าข้อเสียทางเทคนิคล้วน ๆ ของภาษาและสภาพแวดล้อมการพัฒนาอื่นแทบทั้งหมด Python กับ JS แพร่หลายกว่าและมีไลบรารีมากกว่า แต่รู้สึกว่าประสบการณ์พัฒนานั้นแย่กว่ามาก ผมใช้ C#, TS, Py, Hs และภาษาที่ลึกลับกว่านั้นมาเยอะหลายปี แต่ทุกครั้งที่ติดหล่มกับภาษาเหล่านั้นหรือหงุดหงิดอย่างหนักกับสิ่งที่แย่อย่างเห็นได้ชัด ก็จะกลับมาหา CL(SBCL + Emacs + Slime) แล้วรู้สึกโล่งใจ และมั่นใจว่าโลกนี้ยังมีสิ่งดี ๆ อยู่บ้าง
    ตอนนี้เรากำลังระดมทุนให้ผลิตภัณฑ์ที่เราโง่พอจะ bootstrap ด้วย TypeScript แต่เวอร์ชันเปิดตัวจะทำใหม่ด้วย CL นั่นหมายความว่าในอีก 3–5 ปีก่อนเปิดตัว เราจะทำงานด้วย CL และภายใน CL ซึ่งก็ดีเพราะจะได้ทำงานสนุก ๆ อย่างการ implement DSL, การสร้างโค้ด, การทำงานกับ macro และการ implement ตัววิเคราะห์ static type
    • Lisp ที่ไม่มี cons เป็นไปไม่ได้
      เวลาใช้ C# มีอะไรใน Lisp ที่คิดถึงบ้าง?
    • ทำไม TypeScript ถึงไม่เหมาะกับผลิตภัณฑ์นั้น?
  • ในบล็อกและคอมเมนต์ของผู้เขียนที่นี่พูดเรื่อง “การเขียนโค้ด” เยอะ แต่แทบไม่เห็นว่าเขาสร้างซอฟต์แวร์ประเภทไหนและมุ่งเป้า use case อะไร
    ความเห็นแบบสุดโต่งของผมคือ เหตุผลที่ functional programming ไม่ได้บูมในวงกว้าง เพราะแม้มันจะดีพอสำหรับการเขียนโปรแกรม แต่ซอฟต์แวร์ส่วนใหญ่ทุกวันนี้ไม่ใช่ “โปรแกรมที่รันจาก command line บนพีซี/เซิร์ฟเวอร์ของฉันแล้วทำงานไปจนจบ” แต่เป็น “โปรแกรมที่เริ่มทำงานแล้วตอบสนองต่ออินพุตของผู้ใช้จนกว่าผู้ใช้จะปิด” หรือ “โปรแกรมที่เริ่มทำงานแล้วตอบสนองต่อเครือข่ายหรือ I/O อัตโนมัติอื่น ๆ จนกว่าซอฟต์แวร์อื่นจะสั่งให้หยุด”
    งานแบบนี้ทำในสไตล์ functional ล้วนได้ยากกว่ามาก หรืออย่างน้อยก็เป็นอย่างนั้นใน implementation แนว functional ที่มีรสนิยมชัดเจนซึ่งผมเคยใช้ เพราะมันไม่ใช่แค่การ evaluate expression บางอย่างอีกต่อไป แต่ต้อง initialize state, ตอบสนองต่อ I/O, อัปเดต state หรือทำ I/O เพิ่ม และอาจถึงขั้นใช้การทำงานแบบขนานเพื่อ monitoring, receiving, ทำ I/O เพิ่ม และอัปเดต state
    แน่นอนว่าไม่ได้เป็นไปไม่ได้ที่จะทำสิ่งเหล่านี้ด้วย Lisp แต่จากประสบการณ์ที่ได้สัมผัส functional programming มาหลายภาคเรียนสมัยปริญญาตรี และใช้ฟีเจอร์ functional ใน C++/Scala อย่างมืออาชีพเพื่อแก้ปัญหาเหล่านี้ ผมรู้สึกว่าการทำให้ functional programming ทำงานได้ดีในแอปพลิเคชันแบบนี้ค่อนข้างยาก และความไม่เหมาะนี้ทำให้ผมลังเลที่จะลงลึกกับ functional programming มากขึ้น
    • ชี้ประเด็นได้ดี ตอนนี้ทั้งหมดเป็นแค่งานอดิเรกล้วน ๆ สิ่งที่ผมสร้างด้วย Common Lisp ก็มีแค่ไคลเอนต์ Battlesnake ธรรมดา ๆ กับเกมสลับคำแบบมัลติเพลเยอร์ที่เพิ่งปิดไปเมื่อวาน ทั้งสองอย่างไม่ได้ได้ประโยชน์ใหญ่โตจากการทำด้วย Lisp แต่ผมได้เรียนรู้เยอะในกระบวนการ และนั่นคือประเด็นสำคัญ

นอกเหนือจาก Common Lisp แล้ว ผมมักต้องเขียนโค้ดที่สร้างโค้ดอีกทีอยู่บ่อย ๆ ในขอบเขตนี้ Lisp น่าจะโดดเด่น แต่ยังไม่มีโอกาสได้ลอง ตัวอย่างโปรเจกต์ล่าสุดที่ทำก่อนจะนึกถึง Common Lisp ก็มีการสร้างโค้ดสำหรับตรวจสอบ JSON Schema เฉพาะใน static site generator และงานสร้าง JSX จาก Markdown เพื่อใช้กับคอนเทนต์เนื้อเรื่องของเกมเขียนโปรแกรม
ยังไม่นับมาโคร C จำนวนนับไม่ถ้วนที่ใช้มาตลอด

  • Common Lisp มี CLOS ซึ่งยังคงเป็นหนึ่งในระบบเชิงวัตถุที่ก้าวหน้าที่สุดอยู่ด้วย Lisp ส่วนใหญ่ไม่ใช่ภาษาเชิงฟังก์ชันแบบ Haskell
  • Elixir ใช้การเขียนโปรแกรมเชิงฟังก์ชัน และยอดเยี่ยมมากเมื่อจำเป็นต้องทำเว็บดีเวลลอปเมนต์หรือระบบที่ทนต่อความขัดข้อง
    และก็ไม่จำเป็นต้องทิ้งฟีเจอร์ดี ๆ ของสไตล์อื่น ๆ ไปทั้งหมด บางส่วนของการเขียนโปรแกรมเชิงฟังก์ชันก็พบได้บ่อยขึ้นเรื่อย ๆ ในภาษาทั่วไป เช่น Rust ก็ใช้แพตเทิร์นเชิงฟังก์ชันในหลายกรณี
    และ Lisp ก็สามารถเขียนแบบเชิงวัตถุหรือเชิงคำสั่งได้ถ้าต้องการ มันไม่ใช่ Haskell
  • ผมใช้เวลาพอสมควรกว่าจะเข้าใจ monad และ IO monad และใช้เวลานานกว่านั้นในการเรียนรู้วิธีประกอบมันอย่างปลอดภัยและควบคุมลำดับการรัน แต่ตอนนี้ผมเขียนแอปพลิเคชันที่ type-safe ได้แล้ว และแม้เวลาทำงานกับภาษาที่ไม่ใช่เชิงฟังก์ชันก็สร้างบั๊กน้อยลง ภาษาที่ผมได้เงินจากการเขียนคือ Java
    ผมมองว่า Lisp เป็นจุดเริ่มต้น และ Haskell คือของจริง แนะนำให้เรียนรู้สไตล์นั้น แม้จะไม่ได้เขียนโค้ดโปรดักชันจริงก็ตาม
  • Common Lisp ไม่ใช่ภาษา “เชิงฟังก์ชันบริสุทธิ์” แต่รองรับทุกพาราไดม์ ของแปลกแบบนี้ก็ยังอนุญาต
    (let ((pair (cons 1 nil)))
    (setf (cdr pair) pair)
    (list (first pair) (second pair) (third pair)))
    ;; => (1 1 1)
  • ถ้าต้องการให้ “รันไปตลอดกาล” ก็เขียนด้วย Rust
    ถ้าต้องการให้ “คอมไพล์ไปตลอดกาล” ก็เขียนด้วย ANSI C
    ถ้าต้องการให้ “อยู่รอดไปตลอดกาล” ก็เขียนด้วย Python 2.7 แล้วทำให้มันเป็นแกนกลางของเทมเพลตอินฟราทั้งองค์กร ได้คะแนนพิเศษถ้าเป็นโมดูล Ansible แบบคัสตอม
  • เหมือนประโยคที่ว่า “นี่ปี 2023 แล้ว แน่นอนว่ากำลังเรียน Common Lisp” ผมเองก็กำลังเรียนอยู่ แต่ฟีเจอร์ที่พูดถึงยังไม่รู้สึกว่ามีประโยชน์เท่าไร พอคุ้นเคยแล้วอาจเปลี่ยนใจก็ได้
    สิ่งที่ผมหวังคือ Lisp จะช่วยลดหรือ abstraction สิ่งจุกจิกกับ boilerplate ให้น้อยที่สุด เพื่อให้ แสดงความคิดออกมาเป็นโค้ด ได้ง่ายขึ้น สำหรับผม Lisp expression ดูเหมือนเป็นวิธีที่เป็นธรรมชาติที่สุดในการแสดงความคิด
    ผมสนใจการเขียนแอป GUI เป็นหลัก เลยอยากเรียน Clog หรือหา/พัฒนา wrapper ดี ๆ บน GUI toolkit สักตัว
  • ประเด็นหลักคือ Scheme กับ Janet ก็ดีมาก แต่ผู้เขียนต้องการ ภาษาที่รันได้แบบสแตนด์อโลน มากกว่า สิ่งที่สร้างความแตกต่างคือ breakloop ซึ่งเป็น REPL แบบเต็มที่เปิดขึ้นเมื่อโปรแกรมเกิดข้อผิดพลาด ไม่ใช่ stack trace หรือ debugger แต่เป็นการสร้างต่อจากจุดที่พังอยู่ตรงนั้นเลย
    • ฟังดูสุดยอดมาก แล้วทำไม Common Lisp ถึงไม่ใช่ภาษาที่ได้รับความนิยมที่สุด? ถามในฐานะคนที่แทบไม่ได้เขียนโค้ด
    • ในฐานะนักพัฒนา Clojure ฟังดูน่าสนใจ แต่สงสัยว่าในกรณีต่อไปนี้จะเป็นอย่างไร นี่ไม่ใช่โค้ดแบบ Lisp แท้ ๆ แค่เป็นตัวอย่างเพื่อให้เห็นจุดที่ไม่เข้าใจ
      (do-it (do-it first))
      (do-it first) ทำงานได้ดี แต่ถ้าการเรียก (do-it (do-it first)) ล้มเหลว จะเกิดอะไรขึ้น?
      ถ้าได้ควบคุมที่จุดที่พังและแก้ definition ของ do-it ได้ก็คงดี แต่ถ้าแก้แล้ว ผลลัพธ์ของ (do-it first) ก็จะเปลี่ยนไปด้วย
      ถ้าอย่างนั้น จุดที่เครื่องอยู่ตอนนี้ก็จะกลายเป็นจุดที่โค้ดปัจจุบันไม่สามารถไปถึงได้อีก
      ผมไม่รู้จริง ๆ ว่ามันทำงานอย่างไรในกรณีที่การแก้ไขซึ่งทำให้ไปต่อได้ กลับเปลี่ยนสถานะที่เราได้รับการควบคุมมาเพื่อแก้นั้นเอง
    • พอได้ยินว่ามี REPL แบบเต็มเปิดขึ้นที่จุดเกิดข้อผิดพลาด และไม่ใช่ stack trace หรือ debugger แต่สร้างต่อจากจุดที่พังอยู่ตรงนั้นเลย ก็ทำให้อยากเปิด Smalltalk image เล็ก ๆ ดู
    • สิ่งนี้เป็นมาตรฐานใน Gambit Scheme ด้วย
    • ของแบบนี้ไม่กลายเป็นช่องทางโจมตีของโค้ดประสงค์ร้ายหรือช่องโหว่ด้านความปลอดภัยในโปรดักชันหรือ?