เหตุใดจึงต้องใช้อัลจีบราอิกเอฟเฟกต์
(antelang.org)- อัลจีบราอิกเอฟเฟกต์ (effect handlers) คือ เครื่องมือควบคุมการไหลของโปรแกรมที่ยืดหยุ่น ซึ่งช่วยให้สามารถทำฟีเจอร์ของภาษาได้หลากหลายรูปแบบ (เช่น การจัดการข้อยกเว้น, generator, coroutine เป็นต้น) ในระดับไลบรารี
- สามารถนำไปใช้กับ การจัดการคอนเท็กซ์, การฉีดดีเพนเดนซี, การแทนที่สถานะโกลบอล ฯลฯ ซึ่งพบได้บ่อยในสาย functional programming
- ช่วยให้ การออกแบบ API เรียบง่ายขึ้น และทำให้การส่งผ่านสถานะ/สภาพแวดล้อมภายในโค้ดเป็นแบบอัตโนมัติ
- ยังมีข้อดีในด้าน การรับประกันความบริสุทธิ์เชิงฟังก์ชัน, ความสามารถในการเล่นซ้ำ, การตรวจสอบความปลอดภัย เป็นต้น
- ด้วยความก้าวหน้าของเทคโนโลยีคอมไพเลอร์ในช่วงหลัง ปัญหาด้านประสิทธิภาพก็ดีขึ้นมาก
ภาพรวมของอัลจีบราอิกเอฟเฟกต์ (Algebraic Effects)
อัลจีบราอิกเอฟเฟกต์ (หรือที่เรียกว่า effect handlers) เป็นฟีเจอร์ของภาษาโปรแกรมที่กำลังได้รับความสนใจอย่างมากในช่วงหลัง และเป็นหนึ่งในฟีเจอร์หลักของ Ante รวมถึงภาษาเชิงวิจัยหลายตัว (Koka, Effekt, Eff, Flix เป็นต้น) ที่กำลังขยายตัวอย่างรวดเร็ว แม้จะมีเอกสารจำนวนมากอธิบายแนวคิดของ effect handlers แต่คำอธิบายเชิงลึกว่าเหตุใดจึง "จำเป็น" จริง ๆ ยังมีไม่มาก บทความนี้จึงพยายามแนะนำกรณีใช้งานและข้อดีของอัลจีบราอิกเอฟเฟกต์ให้ครอบคลุมมากที่สุด
ทำความเข้าใจไวยากรณ์และความหมายอย่างรวดเร็ว
- อัลจีบราอิกเอฟเฟกต์เป็นแนวคิดที่คล้ายกับ "ข้อยกเว้นที่สามารถดำเนินต่อได้"
- สามารถประกาศฟังก์ชันเอฟเฟกต์ได้ เช่น
effect SayMessage - สามารถระบุได้ว่าฟังก์ชันอาจใช้เอฟเฟกต์ดังกล่าว เช่น
foo () can SayMessage = ... - สามารถจัดการได้ด้วย
handle foo () | say_message () -> ...คล้ายกับ try/catch ของข้อยกเว้น
ด้วยโครงสร้างพื้นฐานลักษณะนี้ จึงสามารถเรียกใช้และควบคุมเอฟเฟกต์ได้
การขยายการควบคุมการไหลแบบกำหนดเอง
เหตุผลสำคัญที่สุดของอัลจีบราอิกเอฟเฟกต์คือ ฟีเจอร์ภาษาเพียงอย่างเดียวนี้สามารถทำสิ่งที่เดิมต้องอาศัยฟีเจอร์ภาษาหลายชนิดแยกกัน (เช่น generator, exception, coroutine, async) ให้กลายเป็น สิ่งที่สร้างได้ในรูปไลบรารี
- หากใส่ตัวแปรเอฟเฟกต์แบบพหุสัณฐานให้ฟังก์ชัน (
can e) ก็จะสามารถส่งต่อและผสมเอฟเฟกต์หลายแบบผ่านอาร์กิวเมนต์ของฟังก์ชันได้ - ตัวอย่างเช่น ฟังก์ชัน
mapสามารถประกาศให้ฟังก์ชันที่รับเข้ามาใช้เอฟเฟกต์ใด ๆeได้ ทำให้ผสานกับเอฟเฟกต์หลากหลายรูปแบบ (การแสดงผล, async ฯลฯ) ได้อย่างเป็นธรรมชาติ
ตัวอย่างการทำ exception และ generator
- การทำ exception: หากเกิดเอฟเฟกต์ขึ้นแล้วจัดการโดยไม่เรียก
resumeก็จะทำงานเหมือนข้อยกเว้น - การทำ generator: นิยามเอฟเฟกต์
Yieldแล้วเมื่อมีการ yield ค่าแต่ละครั้ง แฮนด์เลอร์ภายนอกจะเข้ามาแทรกเพื่อควบคุมโฟลว์ตามเงื่อนไขได้ ทำให้เขียนแพตเทิร์นขั้นสูงอย่างการกรองข้อมูลได้ด้วยโค้ดในระดับที่เรียบง่าย
ความสามารถในการผสมเอฟเฟกต์หลายชนิดเข้าด้วยกันก็เป็นข้อได้เปรียบสำคัญเมื่อเทียบกับเทคนิค abstraction ของเอฟเฟกต์แบบเดิม
การใช้งานในฐานะชั้น abstraction
อัลจีบราอิกเอฟเฟกต์ไม่ได้มีประโยชน์แค่การขยายความสามารถแกนหลักของการเขียนโปรแกรมเท่านั้น แต่ยังใช้งานได้ดีมากในสถานการณ์ของแอปพลิเคชันธุรกิจที่หลากหลาย
การฉีดดีเพนเดนซี (Dependency Injection)
- สามารถทำ abstraction ของออบเจ็กต์ที่ต้องพึ่งพา เช่น ฐานข้อมูลหรือการแสดงผล ให้อยู่ในรูปเอฟเฟกต์และ จัดการผ่านแฮนด์เลอร์ได้
- จึงสามารถสลับเป็น mock สำหรับการทดสอบ หรือทำ output redirection ได้อย่างยืดหยุ่น
การจัดการล็อกหรือการแสดงผลแบบมีเงื่อนไข
- สามารถควบคุมจากศูนย์กลางได้ว่าจะให้แสดงข้อความล็อกหรือไม่ตามระดับ logging
ทำให้การออกแบบ API เรียบง่ายขึ้นและส่งผ่านคอนเท็กซ์โดยอัตโนมัติ
การใช้ state effect
- ในสถานการณ์ที่ต้องส่งผ่านออบเจ็กต์คอนเท็กซ์หรือข้อมูลสภาพแวดล้อม หากทำให้ใช้งานผ่านเอฟเฟกต์แบบ
get/setเท่านั้น ก็จะสามารถ จัดการสถานะได้แบบอัตโนมัติโดยไม่ต้องส่งผ่านอย่างชัดเจน - เดิมทีต้องส่ง context เป็นอาร์กิวเมนต์ให้ทุกฟังก์ชัน แต่ state effect สามารถซ่อนส่วนนี้ได้
การแทนที่ออบเจ็กต์โกลบอล
- แม้แต่สถานะที่เดิมจัดการผ่านออบเจ็กต์โกลบอล เช่น ตัวสร้างเลขสุ่มหรือการจัดสรรหน่วยความจำ ก็สามารถทำ abstraction เป็น effect ได้ ซึ่งได้เปรียบในด้านความชัดเจนของโค้ด ความสะดวกในการทดสอบ และการรองรับ concurrency
- เพียงแค่สลับแฮนด์เลอร์ ก็สามารถเปลี่ยนแหล่งที่มาของเลขสุ่มจริงได้อย่างยืดหยุ่น
รองรับการเขียนแบบ direct style
- เดิมทีต้องรับมือกับการซ้อนกันของ option type, การห่อข้อผิดพลาด และโครงสร้างอื่น ๆ อีกหลายชั้น
- เอฟเฟกต์ช่วยให้สามารถแสดงเส้นทางของข้อผิดพลาดหรือผลข้างเคียงได้อย่างสะอาด โดยไม่ต้องพึ่งการห่อแบบดังกล่าว
การรับประกันความบริสุทธิ์และการตรวจสอบความปลอดภัย
การระบุผลข้างเคียงอย่างชัดเจน
- ในภาษาที่รองรับ effect handlers ส่วนใหญ่ ฟังก์ชันที่ก่อให้เกิดผลข้างเคียงจะต้องระบุเอฟเฟกต์อย่าง
can IO,can Printเป็นต้น ไว้ใน type signature - ในกรณีอย่างการสร้างเธรดหรือ software transactional memory (STM) จำเป็นต้องใช้ฟังก์ชันที่บริสุทธิ์เท่านั้น
การ replay ล็อกและเครือข่ายเชิงกำหนดได้
- เมื่ออาศัยความบริสุทธิ์ ก็สามารถสร้างแฮนด์เลอร์อย่าง
record,replayเพื่อทำให้ผลการรันสามารถทำซ้ำได้ - จึงรองรับผลลัพธ์แบบกำหนดได้และการ rollback สำหรับ การดีบัก, ฐานข้อมูล, เครือข่ายเกม เป็นต้น
รองรับความปลอดภัยแบบ Capability-based
- เอฟเฟกต์ทั้งหมดที่ยังไม่ได้ถูกจัดการจะปรากฏใน type signature ของฟังก์ชัน จึงมีประโยชน์มากเมื่อตรวจสอบความปลอดภัยของไลบรารีภายนอก
- หากฟังก์ชันที่เดิมไม่มีผลข้างเคียงถูกอัปเดตให้มี
can IOเพิ่มเข้ามา โค้ดที่เรียกใช้ก็จะตรวจพบได้ทันที
อย่างไรก็ตาม เนื่องจากเอฟเฟกต์ทั้งหมดสามารถแพร่กระจายต่อไปได้โดยอัตโนมัติ จึงมีโอกาสเกิดผลข้างเคียงจากการที่เอฟเฟกต์ถูกจัดการโดยไม่ตั้งใจได้เช่นกัน
มุมมองด้านประสิทธิภาพและบทสรุป
- ในอดีต ปัญหาด้านประสิทธิภาพเคยเป็นจุดอ่อน แต่ปัจจุบันในหลายกรณี เช่น tail-resumptive effects ได้มี ความก้าวหน้าอย่างมากในด้านการปรับให้เหมาะสม
- แต่ละภาษายังสามารถใช้กลยุทธ์การคอมไพล์ที่เหมาะสมของตนเองได้ เช่น closure call, evidence passing, การทำ handler specialization เป็นต้น
คาดกันว่าอัลจีบราอิกเอฟเฟกต์จะมีบทบาทที่สำคัญยิ่งขึ้นมากในภาษาโปรแกรมแห่งอนาคต
1 ความคิดเห็น
ความคิดเห็นใน Hacker News
ผมมองว่ามีข้อเสียอยู่สองอย่าง
อย่างแรกคือเมื่อดูโค้ดชิ้นที่ให้มา ไม่มีอะไรบอกเลยว่า
fooหรือbarอาจล้มเหลวได้ถ้าจะรู้ว่าการเรียกแบบนี้อาจทำให้ตัวจัดการข้อผิดพลาดทำงาน ก็ต้องไปเปิดดู type signature เอง และบางกรณีก็ต้องทำด้วยมือหากไม่มี IDE ช่วย
อย่างที่สองคือ หลังจากรู้แล้วว่า
fooกับbarอาจล้มเหลว ถ้าจะหาว่าตอนล้มเหลวจริง ๆ โค้ดส่วนไหนจะถูกรัน ก็ต้องไล่ขึ้นไปตาม call stack ไกลพอสมควรเพื่อหาwithexpression แล้วจากนั้นค่อยไล่ลงมาตาม handler นั้นมันตามพฤติกรรมนี้แบบสแตติกหรือกดกระโดดไปยังนิยามใน IDE ตรง ๆ ไม่ได้ เพราะ
my_functionอาจถูกเรียกจากหลายที่ด้วย handler ที่ต่างกันผมคิดว่าแนวคิดนี้สดใหม่มาก แต่สุดท้ายก็ยังมีความกังวลในแง่ความอ่านง่ายของโค้ดและการดีบัก
สำหรับประเด็นเรื่องการหาว่าโค้ดไหนทำงานเมื่อการรันล้มเหลว มีคำอธิบายว่านี่แหละคือหัวใจของ dynamic code injection
มันเป็นโครงสร้างที่ผูก binding ตาม call stack เช่นเดียวกับความสามารถเชิงพลวัตหลายแบบอย่าง shallow-binding, deep-binding เป็นต้น
ที่ทำ static analysis หรือกระโดดใน IDE ไม่ได้ก็เพราะคุณสมบัติความเป็น dynamic นี่เอง
แต่ก็คิดว่าในกระบวนการนี้จริง ๆ แล้วไม่ได้มีอะไรที่ต้องกังวลมากนัก
เพราะมันเป็นการเพิ่ม effect ให้กับ pure code ทำให้สามารถนำไปต่อเข้ากับบริบทต่าง ๆ ได้ ทั้ง pure effect, impure effect, mock สำหรับทดสอบ หรือ production environment
หลักการคล้ายกับ dependency injection
ใน monad แบบดั้งเดิมก็ทำคล้ายกันได้ แต่ถ้าจะหาว่า monad ถูก instantiate ที่ไหนจริง ๆ ก็ต้องย้อนดู call stack เหมือนกัน
เทคโนโลยีแบบนี้มีข้อดีที่ให้มา แต่ก็มีต้นทุนที่ชัดเจนเช่นกัน
เหมาะกับงานอย่างการทดสอบและ sandboxing แต่ก็มีคุณสมบัติที่ทำให้สิ่งที่เกิดขึ้นในโค้ดไม่ได้มองเห็นชัดนัก
มีคนแชร์ว่าตัวเองเคยเขียนวิทยานิพนธ์ระดับปริญญาตรีเกี่ยวกับการรองรับ lexical effects และ handlers ใน IDE
และคิดว่าทุกประเด็นที่ชี้ไว้ข้างต้นสามารถทำให้เกิดขึ้นได้จริง
ลิงก์วิทยานิพนธ์
มีการพูดถึงแนวโน้มการใช้อินเทอร์เฟซมากเกินไปบน .NET ซึ่งทำให้การกระโดดไปยัง implementation ของเมธอดต้องผ่านหลายขั้นตอนจนยุ่งยาก
บ่อยครั้งถ้า implementation อยู่ใน assembly อื่น ความสามารถของ IDE ก็แทบใช้ไม่ได้
ใน Dependency Injection ขั้นสูง (เช่น Autofac) ยังมีการสร้าง scope แบบลำดับชั้นคล้ายตัวแปร dynamic scope ใน LISP เพื่อกำหนดตอนรันไทม์ว่าบริการจะถูก bind กับ instance ไหน
ในแง่นี้ effect ก็อาจแทนได้ด้วยการ inject instance ของอินเทอร์เฟซอย่าง
ISomeEffectHandlerและเมื่อเกิด effect ก็แทนด้วยการเรียกเมธอดนั้นพฤติกรรมจริงของ handler (เช่น throw exception, logging ฯลฯ) จะถูกกำหนดแบบ dynamic ตามการตั้งค่า DI
เดิมทีเคยใช้แพตเทิร์นโยน exception แต่ก็สามารถเปลี่ยนมาเป็นดีไซน์ที่ประกาศ effect ผ่านอินเทอร์เฟซและปล่อยให้ DI ตัดสินวิธีจัดการทั้งหมดได้
ส่วนเรื่องที่เกี่ยวกับ iterator อย่าง
yieldยังไม่ได้เจาะลึกคิดว่าจุดสำคัญคือส่วนที่ไม่ได้แสดงว่า
fooกับbarอาจล้มเหลวได้ทำให้สามารถเขียนโค้ดใน direct style ได้โดยไม่ต้องสนใจบริบทเชิง effect
การตามหาโค้ดที่จะทำงานเมื่อเกิดความล้มเหลวก็เป็นธรรมชาติของ abstraction
ว่าตอนรันจริง effect handler ตัวไหนจะถูกผูกเข้ามานั้นเป็นสิ่งที่ตัดสินทีหลัง
หลักการคล้ายกับใน
f : g:(A -> B) -> t(A) -> Bที่เราไม่อาจรู้ล่วงหน้าได้ว่าเมื่อgทำงานจริงจะมีโค้ดใดถูกรันไม่เห็นด้วยกับข้ออ้างที่ว่าการไล่ขึ้นตาม call stack เพื่อหา handler นั้นทำ static analysis ไม่ได้
ความเห็นนี้บอกว่าในทางปฏิบัติทำ static analysis ได้ และใช้ความสามารถอย่าง "ไปยัง caller" ใน IDE เพื่อเลือกดูได้ว่า handler ไหนถูกใช้งาน
"pseudocode" ของ Ante น่าประทับใจมาก
ให้ความรู้สึกเหมือนเอาคุณสมบัติของ Haskell มาผสมกับพลังการแสดงออกและความใช้งานจริงของ Elixir ได้อย่างลงตัว
เหมือน Haskell สำหรับนักพัฒนา
หวังว่าคอมไพเลอร์จะพัฒนาเต็มที่
และอยากลองพัฒนาแอปด้วย Ante
ต่อประเด็นที่ว่า AE (Algebraic Effects) เป็นการทำให้ control flow เป็นนามธรรมทั่วไปจนสามารถใช้สร้าง coroutine ได้
มีคนมองว่าวิธีที่ง่ายที่สุดในการทำ AE ในภาษาใหม่จริง ๆ ก็คือใช้ coroutine แล้วใส่ไวยากรณ์ของ effect ครอบบนโครงพื้นฐาน
yield/resumeจึงถามว่าตัวเองกำลังพลาดอะไรไปหรือไม่
จุดต่างสำคัญของ AE เมื่อเทียบกับ coroutine คือ type safety
ใน AE สามารถระบุได้ว่าฟังก์ชันหนึ่งใช้ effect อะไรได้บ้างจากในซอร์สโค้ด
ตัวอย่างเช่น ถ้าเป็น
query_db(): User can Databaseก็แปลว่าสามารถเข้าถึงฐานข้อมูลได้ และเวลาถูกเรียกต้องมีDatabasehandler มาให้เสมอโครงสร้างแบบนี้ทำให้ข้อจำกัดว่าอะไรทำได้หรือทำไม่ได้ชัดเจนมาก
คล้ายกับที่ใน NextJS server component ใช้ความสามารถฝั่ง client โดยตรงไม่ได้ ความปลอดภัยเชิงข้อจำกัดแบบนี้จึงเป็นที่นิยมในหลายด้าน
Effect-TS เข้าใกล้วิธีนี้บน JavaScript (การใช้ coroutine) แต่สุดท้ายก็ไม่แน่ใจว่านี่เป็นไอเดียที่ดีจริงหรือไม่
มีความกังวลว่าเหมือนกับ DI ของ Spring ที่ AE อาจแพร่กระจายไปทั่วทั้งโค้ดเบสและสุดท้ายสร้างแต่ความซับซ้อน
ถึงขั้นวิจารณ์ว่างานพูดที่ EffectDays ซึ่งแนะนำวิธีใช้ effect บนฝั่ง frontend ส่วนใหญ่เต็มไปด้วย boilerplate ที่แทบไม่มีความหมาย
แม้ AE จะเป็นแนวคิดที่ชวนหลงใหล แต่ภาระที่ต้องห่อหลายอย่างให้เป็นฟังก์ชันอาจบั่นทอนความเขียนโค้ดง่ายแบบเฉพาะของ JS
ในทางกลับกัน แนวทางอย่าง motioncanvas ที่ใช้แค่ coroutine ก็มีจุดแข็งมากในการอธิบายสถานการณ์กราฟิก 2D ที่ซับซ้อนได้อย่างง่ายดาย
วิดีโอที่เกี่ยวข้อง EffectDays
MotionCanvas
มีข้ออ้างว่าในเธรดเดียวกัน AE handler สามารถ
resumeโค้ดได้หลายครั้งเหมือนcall/ccในขณะที่ coroutine จะ resume ได้เพียงครั้งเดียวต่อการ
yieldหนึ่งครั้งแต่เพราะการไหลของการทำงานที่ไม่แน่นอนแบบนี้กลับทำให้คาดเดายากขึ้น จึงมีคนบอกว่าตัวเองชอบวิธีคืนฟังก์ชันที่เรียกได้หลายครั้งแบบ explicit หรือแทนด้วยโครงสร้างอื่นอย่าง iterator มากกว่า
มีมุมมองว่าแนวคิดนี้น่าดึงดูดมากในฐานะ abstraction สำหรับการเขียนโค้ด
ตอนทำ kernel programming ที่ Sun รู้สึกว่ามันมีข้อดีมากเพราะสามารถเขียนโค้ดได้กระชับเมื่อเรียกอะไรอย่าง
sleep(foo)แล้วถูกfooปลุกให้ตื่นกลับมาทำงานต่อทำให้ภาระในการจัดการ edge case ต่าง ๆ ด้วย control flow ทีละอย่างลดลง
ถ้าระวังประเด็นเรื่อง memory locality ให้ดี ก็ดูเหมือนจะสนุกกับการ initialze หลายฟังก์ชันไว้ล่วงหน้าในสถานะรอ และแสดงอัลกอริทึมเป็นการเปลี่ยนแปลงของแต่ละยูนิตได้โดยตรง
ต่อคำกล่าวว่า "algebraic effects คือ exception ที่ resume ได้"
มีคำถามว่ามันต่างจาก type class อย่าง
ApplicativeErrorหรือMonadErrorจริง ๆ ตรงไหนวิธีประกาศว่าในฟังก์ชันใช้ effect อะไรได้บ้างก็ดูคล้าย checked exceptions และการจัดการ effect ผ่าน
handleexpression ก็แทบเหมือนtry/catchtype class พวกนี้ก็รองรับการจับ exception ผ่าน
handleError/handleErrorWithอยู่แล้วแม้จะบอกว่า algebraic effects มีข้อดีที่จะนำไปใช้กับภาษาของ "อนาคต" แต่ในความจริงมันก็ดูเป็นแนวคิดที่ใช้งานกันได้อยู่แล้วในปัจจุบัน
ลิงก์อธิบาย cats
ถ้าจัดการแค่ effect เดียว ความแตกต่างอาจไม่มากนัก แต่เมื่อจำเป็นต้องใช้หลาย effect พร้อมกัน การรองรับ effect โดยตรงจะสะอาดและเข้าใจง่ายกว่าการซ้อน monad แบบ explicit มาก
เมื่อรวม monad เข้าด้วยกัน มักมีปัญหาปวดหัวเรื่องการกำหนดลำดับ หรือการสลับลำดับเมื่อผลลัพธ์ของบางฟังก์ชันไม่ตรงกับชุด monad ที่คาดไว้
โดยส่วนตัวคิดว่า monad กับ effect ไม่ได้เป็นคู่แข่งกัน แต่ควรมองเป็นกรอบการตีความที่เสริมกันมากกว่า
ดูบทความที่เกี่ยวข้องได้ เช่น งานเขียนเกี่ยวกับ Koka
algebraic effects ทำงานบน program stack คล้าย delimited continuation
ด้วย monad trick ธรรมดาเพียงอย่างเดียว จะทำสิ่งอย่างกระโดดขึ้นไปยัง effect handler ที่อยู่สูงกว่า 5 stack frame ทันที แล้วแก้เฉพาะ local variable ใน frame นั้น ก่อนย้อนกลับลงมา 5 ชั้นไม่ได้
ความต่างคือพฤติกรรมแบบ static เทียบกับ dynamic
เวลาทำโปรแกรมด้วย monad ต้อง implement เมธอดที่เกี่ยวข้องทั้งหมดด้วยตัวเอง แต่ใน effect system เราสามารถติดตั้ง effect handler แบบ dynamic ได้ทุกเมื่อ และ override handler เดิมได้อย่างยืดหยุ่น
ตัวอย่างเช่น อาจใช้ monad เฉพาะที่มีลักษณะ IO สำหรับการทดสอบในระดับล่าง และติดตั้ง effect handler เพิ่มเติมเฉพาะข้างใต้จุดนั้นก็ได้
แม้จะคล้ายกันมาก แต่ต่างกันที่ความใช้งานสะดวก
algebraic effects มีโครงสร้างคล้าย
freemonad แต่เป็นสิ่งที่มีมาให้ในภาษา จึงเขียนไวยากรณ์ได้ง่ายกว่าและทำ composability ได้ดีกว่าในภาษาแนว monad อย่าง Haskell ก็สามารถให้ผลคล้ายกันได้บ้างผ่านการอนุมาน type class (
mtlstyle) และbindที่ฝังมาในไวยากรณ์เดิมทีเข้าใจผิดว่า algebraic effects มีเฉพาะใน static type system เท่านั้น แต่ไม่นานมานี้เพิ่งรู้ว่ายังมีโครงสร้างแบบ dynamic ด้วย
บทความสองชิ้นเกี่ยวกับเวอร์ชัน dynamic ของ Eff ที่เคยอ่าน (ชิ้นแรก, ชิ้นที่สอง) สร้างความประทับใจมาก
แนวคิดอย่าง "parameterized operations with generalized arity" ก็รู้สึกว่าน่าสนใจมากเมื่อเชื่อม abstraction เข้ากับการเขียนโปรแกรม
มีการพูดถึงว่าแนวคิดเก่าแก่กำลังกลับมาอีกครั้งในชื่อและกรอบใหม่
แนะนำ LISP Condition System
ลองสัมผัส Algebraic Effects
เคยลองทำ protohackers ด้วย effects บน OCaml 5 alpha
สนุกดี แต่ตอนนั้น toolchain ค่อนข้างไม่สะดวก
Ante ให้ความรู้สึกคล้ายกัน จึงคาดหวังกับพัฒนาการต่อจากนี้
แม้ยังไม่มี type system มารองรับ แต่ตอนนี้ดูสะอาดลงอย่างชัดเจน
ใช้เวลาอยู่กับ Prolog มามาก และกำลังมองหาภาษาที่ช่วยให้ compose ฟังก์ชันแบบ non-deterministic และทำ compile-time type check ได้ง่าย
Ante จึงเป็นหนึ่งในตัวเลือกที่สนใจ
อีกคอมเมนต์ยังเสริมว่าอย่าลืมเครื่องมือสำหรับนักพัฒนาและปลั๊กอินเอดิเตอร์อย่าง LSP หรือ tree-sitter ด้วย
และคิดว่าเครื่องมือเป็นสิ่งจำเป็นตั้งแต่ช่วงต้นของภาษาใหม่
เนื่องจากให้ความสำคัญกับประสบการณ์ดีบักด้วย จึงกำลังพิจารณาว่าจะใส่ความสามารถ replayability เป็นค่าเริ่มต้นอย่างน้อยใน debug mode ได้หรือไม่
ต่อคำกล่าวว่า "algebraic effects คือ exception ที่ resume ได้"
มีคนถามว่ามันคล้ายกับ Common Lisp conditions ไหม
และรู้สึกว่าน่าสนใจที่แนวคิดเก่ากลับมาปรากฏใหม่เพียงแค่เปลี่ยนชื่อ
algebraic effects ครอบคลุมกว่าระบบ condition ของ LISP มาก
continuations มีลักษณะ multi-shot ได้ ซึ่งคล้าย
call/ccของ Schemeและยังมีการพูดด้วยว่าความขนานในลักษณะนี้อาจทำให้เกิดผลลัพธ์ที่แย่กว่าการไม่มีมันเสียอีก
ใน Smalltalk มีสิ่งที่เรียกว่า "resumable exceptions"
ถ้ามอง effect แค่ว่าเป็นการเปลี่ยนชื่อจาก condition system แบบเก่า ก็จะคุยกันต่อได้ยาก
algebraic effects ที่กำลังพูดถึงกันอยู่ตอนนี้มีความแตกต่างมากกว่าระดับแนวคิดพื้นฐานอย่างชัดเจน
Dependency Injection เองก็อาจถูกพูดถึงได้ในบริบทคล้ายกัน