- Hurl เป็นภาษาโปรแกรมเชิงทดลองที่สร้าง โฟลว์การควบคุมด้วยการจัดการข้อยกเว้นเท่านั้น แทนการใช้การแตกแขนงหรือการวนซ้ำแบบทั่วไป
- แนวคิดนี้เริ่มจากบทสนทนาระหว่าง Nicole Tietz-Sokolskaya กับเพื่อน ๆ ที่ Recurse Center และเว็บไซต์มีทั้งเอกสารการใช้งาน ตัวอย่าง คำแนะนำการดีบัก และคำถามคำตอบ
- ข้อความแนะนำบนหน้าแนะนำใช้โทน ล้อเล่นอย่างจงใจ อย่างเช่น “monstrosity is beautiful” และ “Certified unhinged™”
- แม้ Hurl และซอร์สโค้ดของเว็บไซต์จะเปิดเผยสู่สาธารณะ แต่หากจะส่งแพตช์ทางอีเมลจะต้องมีการ โอนสิทธิ์ในแพตช์
- ไลเซนส์มีให้เลือกแบบ AGPL-3.0, GAL-1.0 และไลเซนส์เชิงพาณิชย์
การทดลองภาษาที่เหลือไว้เพียงการจัดการข้อยกเว้น
- Hurl ถูกสร้างขึ้นเพื่อเป้าหมายเดียวคือสำรวจว่า ภาษาที่ประกอบขึ้นจาก โฟลว์การควบคุมแบบอิงการจัดการข้อยกเว้น เพียงอย่างเดียวจะเป็นไปได้หรือไม่
- ไอเดียนี้มาจากบทสนทนาระหว่าง Nicole Tietz-Sokolskaya กับเพื่อน ๆ ที่ Recurse Center โดยไม่ได้เปิดเผยตัวตนของเพื่อนเหล่านั้น “เพื่อรักษาเกียรติ”
- เว็บไซต์มีเอกสารการใช้งาน Hurl ตัวอย่าง และ คำแนะนำการดีบัก รวมถึงคำถามคำตอบ
ดูเหมือนเรื่องล้อเล่น แต่เป็นโปรเจ็กต์ที่เผยแพร่จริง
- หน้าแนะนำมีคำรับรองที่สะท้อนคาแรกเตอร์ของ Hurl
- “This monstrosity is beautiful, and I must never touch it”
- “Certified unhinged™!”
- “is "🤮" an available quote?”
- หากต้องการส่งคำรับรองเพิ่มเติม ต้องส่งอีเมลถึง Nicole และต้องยินยอมอย่างชัดแจ้งให้นำคำพูดนั้นไปอ้างอิงได้
ซอร์สโค้ดและเงื่อนไขไลเซนส์
- ซอร์สโค้ดของภาษา Hurl และเว็บไซต์นี้เผยแพร่อยู่ที่ Hurl's repo
- หากพบบั๊กหรือข้อผิดพลาด สามารถส่งแพตช์ทางอีเมลได้ แต่ต้องโอนสิทธิ์ทั้งหมดในแพตช์นั้น
- เป็นเงื่อนไขเพื่อคงความสามารถในการรีไลเซนส์และการออก ไลเซนส์เชิงพาณิชย์
- โปรเจ็กต์นี้สามารถใช้งานได้ภายใต้หนึ่งในสามไลเซนส์ต่อไปนี้
-
AGPL-3.0
- GAL-1.0: Gay Agenda License
- ไลเซนส์เชิงพาณิชย์
- ระหว่างกระบวนการพิจารณาไลเซนส์ มีการพิจารณาทั้ง joke licenses และ unfortunate licenses ด้วย แต่สุดท้ายเลือกใช้สามไลเซนส์ข้างต้น
-
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ถ้าจะออกแบบภาษาการเขียนโปรแกรม ก็น่าจะบังคับ namespace กับ include/import และถ้าเป็นไปได้ก็ควรกัน side effect ระดับ top-level ด้วย
รับมาแบบ
let foo = include "lib/foo.hurl"แล้วค่อยเรียกfoo.init()จะทำให้อนุมานได้ง่ายกว่ามากในทางกลับกัน ถ้าเจอ
include "lib/foo.hurl" // side effectsแล้วตามด้วยbaz(buz)ก็จะเดายากว่าฟังก์ชันกับตัวแปรนั้นเป็นของ standard library หรือถูก include มาจากที่ไหนสักแห่งimport "foo/bar"ควรทำให้ใช้foo.*หรือbar.*ได้ ไม่ใช่อยู่ ๆ มีbazz.*โผล่มา ทำให้นึกถึง Go เลยVSCode ก็ทำคล้ายกันได้ผ่านปลั๊กอินกับ LSP แต่ด้อยกว่ามาก ผมทำงานด้วย VSCode ไม่ไหวเพราะการไล่ดูโค้ดช้าเกินไป
เลยสงสัยว่าข้อเสนอแบบนี้จะมีประโยชน์แค่ตอนที่ไม่มีเครื่องมือพวกนั้นหรือเปล่า อย่างน้อยในงานระดับโปรดูเหมือนจะอยู่ไม่ได้ถ้าไม่มีมัน
foo.init()ได้ด้วย ซึ่ง import แบบเปลือย ๆ ทำไม่ได้ไม่ได้หมายความว่าโปรเจกต์นี้ไม่มีคุณค่า ตรงกันข้าม ผมมองว่ามันเป็นงานศิลปะด้วยซ้ำ
เคย fork Ruby แล้วแก้ให้
requireไม่ไปเขียนทับ symbol table แต่สุดท้ายก็หมดความสนใจใน Ruby ไปเลย เพราะ ecosystem ของ Ruby ดูจะพึ่งพา สถานะโกลบอลที่แก้ไขได้ร่วมกัน มากเกินไปผมไม่ค่อยชอบข้อยกเว้นมาตลอด เพราะมันทำให้เข้าใจ สัญญา ระหว่างผู้เรียกกับสิ่งที่ถูกเรียกได้ยากขึ้น และยังเพิ่มความผูกพันของโค้ดด้วย
ผมชอบแนวทางที่จัดการผ่านค่าที่คืนกลับมาแบบ Go หรือ Rust มากกว่า พอมองผ่าน ๆ ภาษานี้แล้วก็ยังไม่แน่ใจว่ามีอะไรที่แก้ปัญหานี้หรือไม่
ถ้า IDE สามารถบอกแบบไดนามิกได้ว่าฟังก์ชันไหนมีข้อยกเว้นที่ยังไม่ถูกจับทั้งหมด และพาไปยังจุดที่อาจมีการโยนข้อยกเว้นได้ โมเดลแบบนี้ก็อาจโอเคได้ แต่ก็ยังไม่รู้ว่าจะจัดการเรื่อง coupling ยังไง และกราฟการควบคุมลำดับการทำงานก็น่าจะไม่เสถียรสุด ๆ
แต่ใน Java ถ้าไม่สืบทอดจาก
RuntimeExceptionข้อยกเว้นที่ฟังก์ชันอาจโยนได้จะถือเป็นส่วนหนึ่งของ signature ของฟังก์ชัน ถ้าโยนโดยไม่เพิ่มไว้ใน signature ก็จะคอมไพล์ไม่ผ่านข้อกำหนดนี้ของ Java ทำให้ IDE รายงานข้อยกเว้นที่ไม่ถูกจับได้ง่ายขึ้นมาก แต่ถ้าไม่ใช่ runtime exception ก็เป็นปัญหาที่แก้ได้ด้วย static analysis
ในทางกลับกัน วิธีคืนค่าแบบ wrapper มาตรฐานอย่าง
Ok/Errดูจะง่ายกว่าทั้งในแง่การรองรับของเครื่องมือและความสะดวกของนักพัฒนาความต่างมีแค่ว่าในแบบส่งต่อข้อยกเว้นนั้น compiler จะช่วยเขียน boilerplate ให้ แต่ถ้าคืนค่าเองคุณต้องเขียนมันด้วยมือ
ผมไม่เข้าใจจริง ๆ ว่าในปี 2024 คนที่ยังมีสมองทำงานปกติจะอยากทำแบบนั้นด้วยมือตัวเองไปทำไม
ตอนที่ใช้ ผมหาสาเหตุรากเหง้า (root cause) ได้เจ็บปวดกว่ามากถ้าไม่มีดีบักเกอร์
ตัวอย่าง
tossเขียนไว้ว่ามัน “ส่วนใหญ่ใช้ส่งหลายค่าออกนอกฟังก์ชัน ไม่ถึงกับจำเป็นแต่ก็น่ารักดี” แต่จริง ๆ แล้วมันไม่ได้ไร้ประโยชน์เลย มันคือการทำ generator ที่ resume ได้แน่นอน ถ้าจะทำให้มันไปทำอย่างอื่นนอกจาก resume ทันที ก็น่าสนใจมากทีเดียว แค่จัดโค้ดทั้ง codebase ให้เป็นสแตกด้านใน-ด้านนอกของ
tossก็พอreturnต้องถูก scope ตาม lexical อยู่ฝั่ง handlerเช่นใน Python คุณจะเรียก
next()จากที่ไหนก็ได้อันนี้ใกล้เคียงกับการส่ง callback ผ่านช่องทางข้างเคียงมากกว่า
tossเป็นตัวเรียก callback นั้น และreturnก็คือคืนค่าจากตรงนั้นตามตัวอักษรในภาษาอื่นมันจะเป็นฟีเจอร์ที่มีประโยชน์จริง ๆ แต่ที่นี่กลับถูกทำเหมือนเป็นฟีเจอร์น่ารัก ๆ ที่ทิ้งได้ เลยไม่แน่ใจว่านี่เป็นมุกหรือเปล่า
yieldของ C# ไหม?Hurl ดูใกล้เคียงกับ ระบบ conditions แบบ Smalltalk หรือ Common Lisp มากพอสมควร
การคลายสแตกกับการ resume เป็นเพียงสองแบบของ restart ที่เป็นไปได้: https://gigamonkeys.com/book/beyond-exception-handling-condi...
แยกจากตัวโปรเจกต์ ผมเชื่ออย่างหนักแน่นว่าโลกจะดีขึ้นถ้ามีอะไรอีกมากมายใช้ นามสกุล .wtf ในโดเมนของมัน
ฟังดูเหมือนรูปแบบที่อ่อนกว่าของ algebraic effects แต่การได้เห็นภาษาลักษณะนี้และสิ่งที่ทำได้ด้วยมันก็ยังเจ๋งอยู่ดี
ถ้า algebraic effects โดยพื้นฐานคืออะไรแบบคีย์เวิร์ด
tossของ Hurl ก็สงสัยว่ามันทรงพลังมากกว่าtossตรงไหนเป็นการทดลองทางความคิดที่น่าสนใจ
ผมเกลียดข้อยกเว้นมาก และอยากได้ภาษาที่ไม่มีข้อยกเว้น
ข้อยกเว้นคือ
gotoแห่งยุคเราเมื่อมี
Maybe/OptionและEffect/Resultอยู่แล้ว ก็แทบไม่มีเหตุผลให้ต้องโยนข้อยกเว้นแล้วตามคิดในหัวว่ามันจะถูกจัดการที่ไหนผมกังวลนิดหน่อยว่า algebraic effects จะยิ่งได้รับความนิยม และส่งผลไปถึง frontend JS ที่ซับซ้อนเกินพออยู่แล้ว เพราะมันส่งเสริมการโยน “ข้อยกเว้น” เพื่อควบคุมลำดับการทำงาน
ถ้าทั้งหมดนี้มีเป้าหมายเพื่อเลี่ยง ปัญหาเรื่องสี ของ async/sync สำหรับผมแล้วมันไม่คุ้มเลย
โอ้โห อันนี้ไม่ชอบเลย แต่แปลกดีที่มันเกือบจะดูสง่างามอยู่เหมือนกัน
มันยากมากที่จะสร้างแบบจำลองในหัว แต่ก็ยังรู้สึกแบบนั้น
ถ้าจะพูดให้จริงจังกว่านี้ ผมอยากให้มี
catchคนละรูปแบบกันในเชิงไวยากรณ์สำหรับข้อยกเว้นที่ resume ได้กับที่ resume ไม่ได้ แบบนั้นจะตัด ความกำกวมทางไวยากรณ์ ว่าreturnจะส่งการควบคุมกลับไปยังฝั่งที่โยนข้อยกเว้นล่าสุดทันทีหรือไม่และ standard library ก็ควรเลิกหนีไปใช้ฟังก์ชันคืนค่าปกติ ถ้ากินของที่ตัวเองทำแล้วแสบท้อง ก็ไม่ใช่เหตุผลที่จะเลิกกินมัน
ถ้าเข้าใจไม่ผิด สิ่งที่ถูก
hurlสามารถจับได้ แต่สิ่งที่ถูกtossจับไม่ได้ ใช่ไหม? คงต้องใช้เวลาหน่อยกว่าจะชินแล้วก็อดกังวลไม่ได้ว่าต้องเขียน Hurl มากแค่ไหนกว่าคนจะเริ่มเรียกผมว่า tosser
Tossฟังดูเหมือนโครงสร้างภาษาที่น่าสนใจ มันไต่ขึ้นไปตามสแตกเพื่อหาตัวจัดการข้อยกเว้น แล้วก็กลับมายังตำแหน่งเดิมเพื่อทำงานต่อราวกับไม่มีอะไรเกิดขึ้นดูเหมือนว่าจะใช้โครงสร้างนี้ ฉีดพฤติกรรมเพิ่มเติม เข้าไปตอนรันไทม์ได้
ปกติในโค้ดเชิงวัตถุเราจะทำ dependency injection ผ่าน constructor ของ service แต่
tossทำสิ่งนั้นได้ผ่าน “toss handler” ใช่ไหม?https://koka-lang.github.io/koka/doc/book.html#why-handlers
ผมไม่ได้รู้เยอะนัก แต่รู้ว่ามันช่วยให้ฉีดพฤติกรรมตอนรันไทม์ได้ในลักษณะนี้
Resume Nextของ VB