- Flix เป็นภาษาแนวใหม่ที่ผสานการเขียนโปรแกรมเชิงฟังก์ชันเข้ากับ โมเดลเชิงเอฟเฟกต์
- เหมาะสำหรับการจำลองกฎเชิงตรรกะและ การพึ่งพาข้อมูล โดยมีจุดเด่นที่การแสดงความรู้แบบประกาศ
- สามารถเขียน ความสัมพันธ์แบบพึ่งพา ที่ซับซ้อนและลำดับการไหลของกระบวนการให้อยู่ในโค้ดอย่างกระชับ
- แนวทางนี้ช่วยเพิ่มประสิทธิภาพในการทำงานด้าน การออกแบบอัลกอริทึมและการอนุมาน
- มีความสามารถด้านคิวรีที่ช่วยให้ สำรวจข้อมูลบนฐานความรู้ได้อย่างง่ายดาย
ภาพรวมของภาษา Flix
- Flix เป็นภาษาฟังก์ชันแบบใหม่ที่นำ กระบวนทัศน์การเขียนโปรแกรมเชิงเอฟเฟกต์ มาใช้
- โปรแกรมเมอร์สามารถอธิบายระบบโดยยึด ความสัมพันธ์และกฎเชิงตรรกะเป็นศูนย์กลาง แทนการเขียนโค้ดเชิงกระบวนวิธี
- ด้วยกฎเชิงตรรกะ (ประกาศภายใน
#{}) จึงสามารถแสดงสถานการณ์การผลิตที่ซับซ้อน เช่น องค์ประกอบ, การพึ่งพา, เวลาประกอบ, กำหนดส่งมอบ ได้อย่างกระชับ
กฎแบบประกาศและการจำลองข้อมูล
- ในโค้ดตัวอย่างมีการใช้ข้อเท็จจริงและกฎ เช่น PartDepends, AssemblyTime, DeliveryDate, ReadyDate
- นิยาม ความสัมพันธ์แบบพึ่งพา ระหว่างผลิตภัณฑ์ เช่น
PartDepends("Car", "Chassis")
- กำหนด เวลาประกอบของชิ้นส่วนแต่ละชิ้น เช่น
AssemblyTime("Engine", 2)
- ระบุ กำหนดส่งมอบ ของชิ้นส่วน เช่น
DeliveryDate("Piston"; 1)
- ผ่านกฎเชิงตรรกะชื่อ ReadyDate จึงสามารถคำนวณวันพร้อมสุดท้ายของชิ้นส่วนที่มีการกำหนดวันส่งมอบไว้ หรือของชิ้นส่วนที่ต้องประกอบได้
- กล่าวคือ สามารถอนุมาน รอบการจัดหาและการประกอบของชิ้นส่วนแต่ละชิ้น ได้อย่างเรียบง่าย
การอนุมานเชิงเอฟเฟกต์และคิวรี
- เอนจินกฎเชิงตรรกะของ Flix ผสาน แนวคิดเชิงเอฟเฟกต์ กับ ความโปร่งใสเชิงอ้างอิง ทำให้การออกแบบโปรแกรมเป็นไปอย่างเข้าใจง่ายและมีข้อผิดพลาดน้อยลง
- ใช้ไวยากรณ์คิวรีเพื่อดึง วันพร้อมของชิ้นส่วนทั้งหมด ที่สอดคล้องกับ ReadyDate ได้อย่างสะดวก
- วิธีนี้สามารถประยุกต์ใช้ได้ในหลายด้าน เช่น การผลิต, การจัดการซัพพลายเชน, ระบบอัตโนมัติที่อาศัยการอนุมาน
สรุปและจุดเด่น
- Flix ผสาน เอฟเฟกต์ (Effects) และการอนุมานบนกฎเชิงตรรกะ เพื่อจำลองความสัมพันธ์ระหว่างองค์ประกอบและกระบวนการของระบบที่ซับซ้อนได้อย่างกระชับ
- เมื่อเทียบกับภาษาเดิม ๆ มี จุดเด่น ที่แตกต่างทั้งในด้านความชัดเจนเชิงตรรกะและความกระชับของโค้ด
- สามารถเป็นโซลูชันที่เหมาะกับปัญหาซอฟต์แวร์สมัยใหม่หลากหลายรูปแบบ เช่น กราฟความรู้, เวิร์กโฟลว์เอนจิน, การอนุมานข้อมูล
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ประทับใจมากกับทั้งความลึกและความกว้างของภาษานี้
ฟีเจอร์สำคัญอย่าง algebraic data types, logic programming, mutability ถูกใส่มาครบตั้งแต่แรก
จากตารางเปรียบเทียบ จุดที่ชอบที่สุดคือมีไฟล์รันได้ตัวเดียวที่ทำหน้าที่เป็นทั้ง package manager, LSP, และ compiler
ในกรณีของ Haskell, LSP ต้องไปทำหลายอย่างซ้ำระหว่าง ghc กับไฟล์ cabal และยังมี stack ถูกใช้งานอยู่ด้วย ทำให้สถานะความเป็นทางการของ package manager ค่อนข้างกำกวม
ไม่ได้ตั้งใจจะวิจารณ์ Haskell เพราะมันเป็นภาษาที่ยอดเยี่ยมมาก
แต่ก็น่าเสียดายที่ฟีเจอร์ที่ดีที่สุดของมันดูเหมือนจะซ่อนอยู่สักหน่อย
อยากรู้ว่า Flix ทำงานร่วมกับ Java และอย่างอื่นบน JVM ได้สะดวกแค่ไหน
เพราะ compiler บน JVM มักลบข้อมูล type ออกไปเกือบหมด แนวคิดเรื่อง 'regions' ของ Flix ที่รองรับการโต้ตอบแบบ imperative เป็นพลเมืองชั้นหนึ่งจึงดูเป็นข้อดี
และการใช้ JVM ก็เป็นข้อได้เปรียบมหาศาล เพราะเข้าถึง standard library คุณภาพสูงระดับมูลค่าหลายหมื่นล้านดอลลาร์ได้ง่าย
เพราะงั้นจึงคิดว่าสำหรับโปรเจกต์มากกว่า 90% JVM หรือ .net core ยังเป็นตัวเลือกที่สมเหตุสมผลที่สุด
ดูเหมือนมีแค่ F# ที่พอจะใช้เป็นภาษาสำหรับเปรียบเทียบได้
ถ้ามีเอกสารสรุปข้อจำกัดของการทำงานร่วมกันระหว่าง Flix กับ JVM ก็คงดีมาก
อนึ่ง มีข้อมูลที่เกี่ยวข้องอยู่ที่นี่
โดยพื้นฐานแล้ว ค่า Flix/Java จะผ่านกระบวนการ boxing/unboxing
และ Record ก็เป็นพลเมืองชั้นหนึ่งด้วย
ถ้าคุณชอบที่ package manager, LSP, และ compiler รวมอยู่ใน executable เดียว ก็น่าจะชอบ Unison มากเหมือนกัน
ส่วน logic programming กับ datalog ให้ความรู้สึกเหมือนถูกเพิ่มเข้ามาอีกชั้นหนึ่ง
ฟีเจอร์อื่น ๆ ดูชัดเจนว่าช่วยเพิ่ม type safety ให้โค้ดเบส แต่ logic programming ดูเป็นฟีเจอร์เฉพาะกลุ่มมาก จึงรู้สึกว่าอาจจะดีกว่าถ้าแยกออกจากตัวภาษา
ที่ว่าคอมไพเลอร์ JVM ลบข้อมูล type ออกหมดนั้นไม่ถูกต้องทั้งหมดนัก (ในกรณีของ anonymous class ยังเก็บ type parameter ไว้)
และก็มีวิธีอ้อมอยู่หลายแบบ
ที่จริงสำหรับ compiler เองก็ไม่ใช่ปัญหาใหญ่
แค่สุ่มชื่อ class ที่ผ่านการใช้ type constructor แล้วเรนเดอร์ออกมาเหมือน class ปกติก็พอ
F# ยังไม่รองรับ type classes จึงมีข้อจำกัดมากในการทำ monad-based programming
ถ้า F# จะข้าม monad แบบ Haskell แล้วไปที่ algebraic effects เลย ก็คิดว่าน่าจะเข้ากับปรัชญาของ F# มากกว่า
ส่วนของ "StringBuilder" ยังรู้สึกติดใจนิดหน่อย
ในแง่นี้มันดูเอนเอียงไปทาง Java มากไปหน่อย เลยยังไม่ค่อยมั่นใจ
แต่ส่วนอื่น ๆ ที่มองผ่าน ๆ ก็ดูโอเค
ในเชิง semantics ของภาษา ดูเหมือน semantics ของการขยาย/จำกัด polymorphic records จะตามแนว scoped label ของ Leijen (ลิงก์บทความ)
เช่น ถ้ามี record ชื่อ r1 = { color = "yellow" } ก็สามารถขยายเป็น r2 = { +color = "red" | r1 } ได้
r2#color จะให้ค่า "red" และถ้าลบฟิลด์ "color" ออกอีกครั้ง ก็จะได้ r3 = { -color | r2 }
และ r3#color จะกลับไปเป็นค่าเดิมคือ "yellow"
คิดว่าวิธีนี้สมเหตุสมผลกว่าสไตล์ก่อนหน้าเยอะมาก ซึ่งต้องใช้ระบบ type ที่ซับซ้อนสุด ๆ เพื่อไม่ให้เพิ่มฟิลด์ label เดียวกันซ้ำสองครั้งได้
สงสัยว่าอะไรทำให้ Aarhus (โดยเฉพาะมหาวิทยาลัย/เทคฮับ) มีอิทธิพลสูงในงานวิจัยด้านภาษาโปรแกรมมิง
C++, C#/Typescript, Dart ล้วนมีรากฐานที่แข็งแรงในพื้นที่เล็ก ๆ แห่งนี้ของเดนมาร์ก
มันไม่ใช่มหาวิทยาลัยชื่อดังแบบ Ivy League หรือ Oxbridge ทั่วไปเหมือน Delft หรือ INRIA
เลยสงสัยเล่น ๆ ว่าอะไรทำให้ที่นี่พิเศษนัก เป็นเพราะน้ำหรือมีเหตุผลอื่น
ขอเสริมอย่างหนึ่งว่า C# ถูกสร้างโดย Anders Hejlsberg และเขาเรียนที่ DTU (โคเปนเฮเกน)
เขายังเป็นคนสร้าง Turbo Pascal ด้วย และ Borland ก็เป็นบริษัทที่ก่อตั้งโดยชาวเดนมาร์ก
โดยรวมแล้วเดนมาร์กแข็งแกร่งมากในด้านทฤษฎีภาษาโปรแกรม
ตัวอย่างเช่น ตำราบัณฑิตศึกษามาตรฐานด้าน static program analysis (โดย Nielson & Nielson) ก็เป็นผลงานของชาวเดนมาร์ก
Mads Tofte ก็มีส่วนสำคัญกับ Standard ML
Aarhus อาจไม่ใช่ระดับ Ivy League หรือ Oxbridge แต่ก็เป็นมหาวิทยาลัยที่ยอดเยี่ยม
ในยุโรปมีมหาวิทยาลัยลักษณะนี้อีกหลายสิบแห่ง คือชื่อเสียงอาจไม่ดังมาก แต่คุณภาพด้านการสอนและวิจัยสูงมาก
Aarhus มีรากฐานแข็งแรงในด้าน logic, type theory, และภาษาสาย functional/object-oriented
นักวิจัยจำนวนมากที่มีอิทธิพลในสาขานี้ก็ออกมาจาก Aarhus
อีกอย่างคือรู้สึกว่างานวิจัยด้านภาษาโปรแกรมมิงทั่วโลกได้รับอิทธิพลจากความเอนเอียงไปทางสหรัฐฯ อย่างมาก
สถาบันอย่าง Aarhus ค่อนข้างไม่เน้นการตลาดหรือการประชาสัมพันธ์ตัวเอง และมักโฟกัสกับงานวิจัยที่ดี
ไม่ได้แปลว่าดีกว่าหรือแย่กว่าเป็นพิเศษ แต่ทำให้ยากต่อการได้รับความสนใจในระดับโลก
Flix ยังคงน่าประทับใจในฐานะภาษาที่ถูกออกแบบอย่างประณีตที่สุดตัวหนึ่งในกลุ่มภาษา ML
การผสมผสานแนวทาง functional, imperative, และ logic พร้อมทั้งมีระบบ polymorphic type & effect และข้อจำกัด Datalog เป็นพลเมืองชั้นหนึ่งนั้นโดดเด่นมาก
การแยกโค้ดบริสุทธิ์/ไม่บริสุทธิ์อย่างเข้มงวดที่ระดับ type ทำให้รู้สึกสดใหม่ในฐานะทางเลือกที่อนุมาน effect ได้ง่ายกว่า Monad
ปรัชญาแบบ "หนึ่งภาษา ไม่ต้องใช้ flags" และการมุ่งให้มีแต่ compile-time errors ก็เรียบง่ายและคาดเดาได้ดี
ทั้งที่ JVM เองไม่รองรับ tail recursion elimination แบบ native แต่ Flix กลับทำ complete tail call elimination ได้ ถือเป็นความสำเร็จทางเทคนิคที่น่าจับตา
อยากรู้ประสบการณ์ของคนที่ใช้ Flix จริงใน production หรือในงานวิจัย
โดยเฉพาะคนที่ใช้ในงานด้าน logic programming หรือ concurrency ว่าเคยเจอปัญหาจาก 'closed-world assumption' หรือการไม่รองรับ exceptions อย่างไรบ้าง และการรวม Datalog นั้นต่างจากภาษา logic อื่นอย่าง Prolog อย่างไร
ตอนก่อนหน้านี้ที่เคยดู Flix รู้สึกว่าน่าสนใจมากจนเขียนบทความชื่อ "Flix สำหรับ Java programmer" ไว้เลย
ตอนนี้อาจจะเก่าไปนิดและควรอัปเดตแล้วก็จริง...
ถ้าสนใจก็ดูได้ที่นี่
บล็อกโพสต์นั้นยอดเยี่ยมมาก
ถ้าได้รับอนุญาต น่าจะดีถ้าเพิ่มเข้าไปในรวมบล็อกอย่างเป็นทางการของ Flix (ลิงก์)
ตั้งแต่บทความนั้นออกมา ภาษา Flix ก็พัฒนาไปมาก
โดยเฉพาะระบบ effect ที่ขยายตัวมากขึ้น การปรับปรุง Java interoperability และการอัปเดต syntax
บล็อกนี้เหมือนขุมทรัพย์เลย
ให้ความรู้สึกเหมือนเป็นเวอร์ชันที่ขัดเกลาขึ้นของความคิดที่ค้างคาใจฉันมานาน
ตื่นเต้นที่จะได้อ่านทั้งหมด
รองรับ HKT ด้วย เจ๋งมาก
แต่ไม่เห็นมีคำอธิบายเรื่อง typeclass เลย เลยสงสัย
ถ้ารองรับ typeclass กับ macro สไตล์ Scala ด้วย ฉันคงลองย้ายไลบรารีที่ทำไว้ (distage, izumi-reflect, BIO) ไป Flix และอาจพิจารณาย้ายจาก Scala ไป Flix อย่างจริงจัง
ต่อมาจึงพบว่า typeclass ในอนาคตเรียกว่า traits
ส่วน macro เป็นอย่างไรบ้างก็ยังสงสัย
และก็น่าเสียดายที่ Flix ไม่รองรับ nominal inheritance แบบ explicit naming ด้วย
เพราะแม้แต่ trait ของ Scala ในรูปแบบที่ไม่เป็นพิษเป็นภัยที่สุดก็ยังใช้ไม่ได้
รู้สึกว่า typeclass ใช้แทน interface ไม่ได้ และถ้าขาด abstraction แบบนั้น ฟีเจอร์ที่มีประโยชน์หลายอย่างก็อาจทำไม่ได้เลยหรือไม่ก็ทำให้โค้ดดูไม่สวย
trait สามารถมี implementation เริ่มต้นของฟังก์ชันได้ แต่ instance ก็ override ได้
Flix ไม่มี inheritance เลย
trait ถูกใช้เฉพาะตอน compile time และผ่านการทำ monomorphization จึงไม่มี runtime overhead
inliner ของ Flix ยัง optimize ภายใน trait ได้ด้วย และกำจัดแม้กระทั่ง closure อย่างจริงจัง
เช่น higher-order function หรือ pipeline จะถูกแปลงเป็น loop ธรรมดาในระดับ bytecode โดยไม่มีการจัดสรร closure หรือการอ้างอิงทางอ้อมเลย
Flix ยังไม่รองรับ macro
ดูเหมือนจะกลัวการนำเข้ามาเพราะประสบการณ์การ (ใช้งานเกินขอบเขต) ในภาษาอื่น
ตอนนี้กำลังมองหาผู้เขียนไลบรารีใหม่ ๆ อยู่ ถ้าสนใจก็แวะมาที่ช่อง Gitter ได้
เกี่ยวกับส่วน 'ฟีเจอร์ที่ Flix ไม่รองรับ' ในเอกสารทางการของ Flix
มีหัวข้อว่า "No Code Before Main"
แต่คำอธิบายจริงเขียนว่า "Flix จะไม่รันโค้ดใด ๆ ก่อน main และไม่มีสิ่งอย่าง static initializer เลย"
คิดว่าเปลี่ยนชื่อฟีเจอร์เป็น "Code Before Main" จะตรงกว่า
สงสัยว่าทำไมใน Flix ถึงต้องบังคับให้ระบุอย่างชัดเจนว่าฟังก์ชันเป็น pure
เพราะดูเหมือนว่าในแทบทุกกรณี static analysis ก็น่าจะอนุมานได้เพียงพออยู่แล้ว เลยอยากรู้เหตุผล
เท่าที่รู้ การระบุความเป็น pure ให้ฟังก์ชันนั้นทำให้ compiler รับประกันเรื่องนี้ได้
จากประโยคที่ว่า "Flix ติดตามความเป็น pure ของทุก expression ในโปรแกรมได้อย่างแม่นยำ" และตัวอย่างนิยามฟังก์ชันที่ไม่มีการระบุ pure/impure
เลยดูเหมือนว่าในกรณีส่วนใหญ่ compiler สามารถอนุมานความเป็น pure ได้เอง
จึงให้ความรู้สึกว่าการระบุว่า pure/impure อาจเป็นตัวเลือกมากกว่า
FAQ ของ Flix (ลิงก์) เริ่มต้นแบบธรรมดาแต่ยิ่งอ่านยิ่งสนุก
ตัวอย่างฮา ๆ บางข้อ:
สงสัยว่า code agent ที่เข้ากับภาษานี้ทำงานได้ดีไหม หรือยังเป็นสถานการณ์ที่ต้องใช้สมองตัวเองอยู่
พูดแบบติดตลก แต่จริง ๆ ภาษาเองก็ดูเจ๋งจนรู้สึกเศร้า
เพราะ LLM น่าจะยิ่งขัดขวางการรับภาษาใหม่มาใช้ แล้วปัญหานี้จะถูกแก้อย่างไรก็ยังสงสัย
ฉันกลับมีลางสังหรณ์ว่า LLM จะช่วยลดกำแพงในการรับภาษาใหม่มาใช้มากกว่า
โค้ด standard library เพียงอย่างเดียวก็มากพอให้ LLM เรียน syntax ใหม่ได้ และถึงไม่พอก็ยังให้ agent เรียนจากการสังเกตผลลัพธ์ของ compiler ได้
งาน porting โค้ดเองก็แทบไม่ต้องใช้ความคิดสร้างสรรค์ระดับสูง เป็นงานที่นิยามชัดเจน จึงน่าจะเป็นงานอัตโนมัติกลุ่มแรก ๆ ของ LLM
ต่อไปเราคงต้องใช้สมองไปคิดอย่างจริงจังมากขึ้นว่า 'เราทำสิ่งนี้ไปทำไม' และ 'มันส่งผลอะไรต่อโลก'
ฉันได้ผลลัพธ์ที่โอเคเมื่อกำชับในพรอมป์ต์ว่าต้องใช้ indexed/dependent types ของ Idris ให้ชัดเจนเสมอ
(ถ้าไม่กำชับแบบนี้ ส่วนใหญ่จะไปได้แค่ระดับ GADT)