ภาษาเพื่อเกรมลิน, Raku
(buttondown.email)- Raku ถูกพิจารณาเป็นตัวเลือกของ ภาษาสำหรับเครื่องคิดเลข เพื่ออุดช่องว่างด้านงานคณิตศาสตร์ที่มีใน Python, J, Frink และ Excel และแม้จะลองเพียงสั้น ๆ ก็ให้ความรู้สึกว่าเป็นภาษาที่ทรงพลังมากแต่ก็ประหลาด
- มี พลังการแสดงออกที่ขับเคลื่อนด้วยโอเปอเรเตอร์ กว้างมาก เช่น สัญลักษณ์ Unicode, infix แบบตัวอักษรและตัวเลข, การคูณลิสต์·zip·reduce·accumulate, ตัวจับคู่
~~, และลำดับ... - ผู้ใช้สามารถนิยามได้ไม่ใช่แค่ infix operator แต่รวมถึง circumfix/postcircumfix operator ด้วย และยังกำหนด associativity ได้ไกลกว่าซ้าย·ขวา ไปจนถึง chain และ list associativity
- Multiple dispatch ไม่ได้แตกแขนงตาม type signature เท่านั้น แต่ยังแตกแขนงด้วยตัวกำหนดเงื่อนไขรันไทม์
whereได้ และทั้ง function signature กับพารามิเตอร์ภายในก็ถูกปฏิบัติเป็นค่าแบบ first-class ได้ - แม้จะดูเป็นภาระหนักสำหรับการบำรุงรักษาโค้ดเบสขนาดใหญ่ แต่ก็น่าสนใจสำหรับ การเขียนโปรแกรมขนาดเล็ก อย่างสคริปต์ใช้ครั้งเดียว งานคำนวณ และเครื่องมือส่วนตัว โดยอุปสรรคหลักคือเอกสาร, Windows REPL, ความเร็วคอมไพล์, และความผิดพลาดเรื่อง sigil
เหตุผลที่ลองดู Raku และความประทับใจแรก
- Raku คือภาษาที่ในอดีตรู้จักกันในชื่อ Perl 6
- หลังจากเขียนถึงความไม่พอใจต่อภาษากลุ่ม dynamic ก็มีผู้ใช้บางคนแนะนำ Raku และจึงลองดูว่าเหมาะจะเป็น ภาษาสำหรับเครื่องคิดเลข สำหรับงานคณิตศาสตร์หรือไม่
- ก่อนหน้านี้ใช้ทั้ง Python, J, Frink, และ Excel ปะปนกัน แต่แต่ละตัวก็มีข้อเสียใหญ่ของตัวเอง
- หลังทดลองอยู่หลายวัน ความรู้สึกที่ได้ใกล้เคียงกับ “ภาษาที่เกรมลินฉลาดมาก ๆ ออกแบบขึ้นโดยรวบรวมฟีดแบ็กจากเกรมลินตัวอื่นจำนวนมาก”
ระบบโอเปอเรเตอร์ที่แปลกตา
- Raku ใช้ Unicode operator อย่างจริงจัง
- ตรวจสอบการเป็นสมาชิกของเซตด้วย
∈ - ยังมี
∉,∋,∌ด้วย
- ตรวจสอบการเป็นสมาชิกของเซตด้วย
- อนุญาต infix operator แบบตัวอักษรและตัวเลขด้วย
- โอเปอเรเตอร์ทำซ้ำสตริงคือ
x - การประกอบฟังก์ชันคือ
o
- โอเปอเรเตอร์ทำซ้ำสตริงคือ
- การผสมลิสต์ก็เขียนด้วยสัญลักษณ์สั้น ๆ ได้
Xสร้าง Cartesian product ของลิสต์Xfนำfไปใช้กับแต่ละสมาชิกของ Cartesian productZfทำแบบเดียวกันในลักษณะ zip
- สำหรับ infix operator
f,[f]จะ reduce ลิสต์ และ[\\f]จะสร้างผลลัพธ์สะสม[+] <1 2 3 4 5>คือ15[\\+] <1 2 3 4 5>คือ(1 3 6 10 15)
ตัวจับคู่ ~~ และลำดับ ...
~~ถูกใช้เป็น matcher ที่จัดการการเปรียบเทียบหลายแบบด้วยไวยากรณ์เดียว"abc" ~~ "abc"ใช้ตรวจสอบการตรงกันของสตริง"abc" ~~ Strใช้ตรวจสอบว่าเป็นชนิดสตริงหรือไม่"abc" ~~ {.chars == 3}ใช้ตรวจสอบว่ามีความยาว 3 หรือไม่"abc" ~~ /^b/ใช้ตรวจสอบว่าabcขึ้นต้นด้วยbหรือไม่
...จะจับแพตเทิร์นจากค่าก่อนหน้าแล้วสร้างเป็น ลำดับ0,1,2...10จะเพิ่มทีละ 1 จาก0ถึง100,2,4...10จะเป็นลำดับเลขคู่1,2,4...10จะตามแพตเทิร์นการเพิ่มแบบ1 2 4 8
โอเปอเรเตอร์ที่ผู้ใช้กำหนดเอง
- Raku ไม่ได้หยุดแค่การนิยาม infix operator แบบบางภาษา แต่ยังสร้าง circumfix และ postcircumfix operator ได้ด้วย
- ตัวอย่างเช่น สามารถนิยามโอเปอเรเตอร์แบบล้อมรอบอย่าง
sub circumfix:<[∀ zz>($inner){sum($inner)}เพื่อรวมค่าด้านในได้ - ยังนิยาม postcircumfix operator ที่ดูเหมือน inner product ของเวกเตอร์ได้ด้วย
sub postcircumfix:<| ⟩>(@left, @inside){[+] (@left Z* @inside)}<1 2 3>|<4 5 6>⟩คือ32
- associativity ของโอเปอเรเตอร์ก็กำหนดได้หลายแบบ
- นิยาม infix operator แบบ left-associative และ right-associative ทั่วไปได้
- กำหนด chain associativity ที่ทำให้
x < y < zถูกตีความเป็นx < y && y < zได้ - รองรับ list associativity ที่ทำให้
a op b op cกลายเป็นop(a, b, c)
Multiple dispatch และการแตกแขนงด้วยเงื่อนไขรันไทม์
- Raku รองรับ multiple dispatch จึงเลือกจากหลายฟังก์ชันนิยามที่มี type signature ต่างกันได้อย่างเหมาะสม
- ฟังก์ชันตัวอย่าง
fทำงานต่างกันตามชุดอาร์กิวเมนต์- ถ้ารับ scalar และ array จะบวก scalar เข้าไปในแต่ละสมาชิกของ array
- กรณีรับ array และ scalar ก็รองรับเช่นกัน
- ถ้ารับ array สองตัว จะบวกสมาชิกทีละตำแหน่งด้วย
Z+
- จุดที่แปลกยิ่งกว่าคือสามารถ dispatch ด้วย ตัวกำหนดเงื่อนไขรันไทม์ ของค่าได้ด้วย
multi my_abs(Int $x where {$x > 0}) {$x}multi my_abs(Int $x) {-$x}
- signature ของฟังก์ชันเป็นค่าแบบ first-class และพารามิเตอร์ภายใน signature ก็เป็น first-class เช่นกัน
ฟีเจอร์เล็ก ๆ ที่รวมกันเป็นพื้นผิวขนาดใหญ่
- ถ้านิยามฟังก์ชัน
MAINพารามิเตอร์ของมันจะถูกแปลงเป็น CLI flag โดยอัตโนมัติ - อ็อบเจ็กต์มีเมธอดที่เตรียมไว้ล่วงหน้าจำนวนมาก
- List object มีเมธอดสำหรับดึงทุก permutation, ทุก k-combination, และทุก sliding window
- Junctions คือรูปแบบค่าที่แปลกออกไปสำหรับการเปรียบเทียบหลายอย่างพร้อมกัน
1|2จะขยายเป็นany(1, 2)ทำให้1 < 1|2เป็นจริง1&2จะขยายเป็นall(1, 2)ทำให้1 !< 1&2เป็นจริง
- infix operator ไหนก็ตามสามารถเติม
!ข้างหน้าเพื่อทำเป็น โอเปอเรเตอร์ปฏิเสธ ได้ - Raku ดูเหมือนเป็นภาษาที่มีทั้งชื่อแบบ
$kebab-caseและ infix ลบไปพร้อมกัน โดยคาดว่า sigil เป็นตัวช่วยแยกx-yออกจากกัน - regex syntax ไม่เข้ากันย้อนหลังกับ Perl 5
- ตลอด 30 ปีที่ผ่านมา ภาษาต่าง ๆ เดินตาม “มาตรฐาน” PCRE แต่ Perl 6 เลือกทิ้งมันไป
พื้นที่ที่ยังไม่ได้ดู และเสน่ห์ในงานขนาดเล็ก
- สิ่งที่ลองดูยังเป็นเพียงบางส่วนของฟีเจอร์ที่เน้น การใช้งานแบบเครื่องคิดเลข
- ยังไม่ได้เรียนรู้ระบบอ็อบเจ็กต์, แพ็กเกจ, หรือ grammars
- ยังมีฟีเจอร์ที่พลาดไปอีกมาก เช่น
samewithในเนื้อฟังก์ชันที่ทำหน้าที่คล้ายเรียกฟังก์ชันเดิมซ้ำด้วยอาร์กิวเมนต์ใหม่ - ถ้าต้องบำรุงรักษาโค้ดเบส Raku แบบ legacy ก็น่าจะยากมาก แต่สำหรับการเขียนโปรแกรมแบบ In The Small นั้นดูทรงพลังมาก
- สคริปต์ใช้ครั้งเดียว
- งานคำนวณ
- เครื่องมือส่วนตัว
- งานประเภทที่เดิมทีต้องการจะใช้มันทำ
ข้อไม่พอใจและความคาดหวัง
- เอกสารยังขาดมาก และเพราะพึ่งพาสัญลักษณ์สูงจึงค้นหาข้อมูลได้ยาก
- แม้จะเคยเรียนภาษาที่เอกสารน้อยมาหลายภาษา แต่ Raku ใหญ่และซับซ้อนกว่านั้นมาก จนอาจทำให้หมดแรงจูงใจได้
- บน Windows ถ้าป้อน Unicode เข้า REPL จะเกิดแครช
- คอมไพเลอร์ก็ค่อนข้างช้า แม้ไฟล์เล็กก็ใช้เวลา มากกว่า 0.5 วินาที ทำให้งานที่ต้องวนทำซ้ำเป็นเรื่องทรมาน
- ระบบ sigil ใช้งานไม่สะดวก
- มีกรณีที่ใช้
$xแทน@xแล้วเสียเวลา 30 นาทีไปกับการดีบักปัญหา
- มีกรณีที่ใช้
- โดยรวมแล้วชอบ Raku และหวังว่ามันจะประสบความสำเร็จ แต่ก็หวังว่าเมื่อเวลาผ่านไป เวลาในการคอมไพล์ และเอกสารจะดีขึ้น
1 ความคิดเห็น
ความเห็นจาก Hacker News
ถ้าวางภาษาโปรแกรมลงบนพื้นที่ 2 มิติ แกนอาจเป็น มันทำให้ประหลาดใจแค่ไหน และเมื่อประหลาดใจแล้ว น่าพอใจ/น่ากลัว แค่ไหน
โดยปริยายแล้ว เรามักคาดหวังว่าภาษาปกติควรอยู่มุมล่างซ้าย คือ “แทบไม่ทำให้ประหลาดใจ แต่ถ้าทำให้ประหลาดใจบ้างเป็นครั้งคราวก็ยังน่าพอใจ” แต่ Raku ให้ความรู้สึกว่าตั้งใจมุ่งไปยังมุมบนซ้ายที่ไม่ค่อยมีใครอยู่แบบเปิดเผยมากกว่า เป็นท่าทีประมาณว่า “แปลกใช่ไหมล่ะ? เจ๋งใช่ไหม?”
document.write = function ...และในแง่ที่มันทำงานที่ต้องการได้ก็ถือว่าน่าพอใจ แต่ในขณะเดียวกันก็ค่อนข้างน่ากลัวด้วยPerl เองก็มี “ความประหลาดใจที่น่าพอใจ” อยู่เยอะ และผมมองว่า Raku ถูกออกแบบมาเพื่อกำจัดความประหลาดใจที่น่ากลัวของ Perl เป็นหลัก
∈เพื่อตรวจว่ามีอยู่ในเซตหรือไม่ ก็เข้าใจแล้วว่าหมายถึงอะไร0,2,4...10จะกลายเป็น(0 2 4 6 8 10)แต่พอ1,2,4...10กลายเป็น(1 2 4 8)ก็ทำให้คิดว่า “นี่กำลังไปหาเลขถัดไปใน OEIS หรือเปล่า?”Raku น่าสนใจในฐานะภาษา แต่ สำนวนการเขียน บางอย่างไม่ค่อยเข้าหัว
คล้ายกับ AppleScript ที่พยายามดูเหมือนภาษาธรรมชาติแล้วกลับให้ความรู้สึกแปลก ๆ มันผสมองค์ประกอบแนวภาษาธรรมชาติอย่าง
my,say,sub,gatherเข้ากับสัญลักษณ์อย่าง@การประกาศโมดูล และการตัดสินใจทางไวยากรณ์ที่คนนอกมองแล้วเหมือนไบแซนไทน์ ตัวอย่าง 99 bottles ก็สามารถไล่ตามเชิงตรรกะได้ แต่ยากที่จะค้นพบได้โดยสัญชาตญาณ รู้สึกว่ามีสัญลักษณ์เยอะและถูก overload ตามบริบท ดังนั้นแม้แต่งานอย่างพาร์เซอร์ภาษาธรรมชาติที่ Raku น่าจะเหมาะมาก ผมก็ไม่ค่อยอยากใช้เองhttps://examples.raku.org/categories/module-management/Fletc...
@ที่หมายถึงอาร์เรย์ จะดูค่อนข้างคุ้นเคยฟีเจอร์ที่ชอบที่สุดใน Raku คือ การหารจำนวนเต็ม และลิเทอรัลทศนิยมต่างก็คืนค่าเป็น
Ratซึ่งเป็นชนิดจำนวนตรรกยะทุกคนรู้ว่าทศนิยมแบบ floating point ไม่ค่อยดี แต่มีภาษาน้อยมากที่พยายามหลีกออกจากมันจริง ๆ และใน Raku ต้องใช้สัญกรณ์วิทยาศาสตร์จึงจะเป็นลิเทอรัล floating point
ภาษาเหล่านี้มีลำดับชั้นตัวเลขที่รวมถึงจำนวนตรรกยะและจำนวนตรรกยะเชิงซ้อน และแน่นอนว่ารองรับตัวเลขความแม่นยำตามอำเภอใจด้วย ความสามารถในการประกอบกันนั้นยอดเยี่ยมมาก
Ratใหญ่เกินไป มันจะเปลี่ยนเป็น floating point โดยอัตโนมัติ1/10เป็นRatแต่1/100000000000000000000กลายเป็นNumมีFatRatที่ไม่ถูกโปรโมตด้วย แต่ไม่ใช่ค่าเริ่มต้นดังนั้นแทนที่จะบอกว่า “มันหลุดพ้นจากวิธีที่ใช้ floating point แบบไม่แม่นยำทั้งที่ไม่ได้ร้องขออย่างชัดเจน” ควรมองว่าเดิมทีมันไม่เคยอยู่ในสภาพนั้นตั้งแต่แรกมากกว่า
(/ 1.0 3.0)ได้0.3333333333333333,(/ 1 3)ได้1/3,(- (+ 0.1 0.2) 0.3)ได้5.551115123125783e-17float()บ่อยกว่าDecimal()มากถ้าไม่ได้จัดการเรื่องเงินโดยตรง เกือบตลอดเวลา floating point ก็คือตัวเลือกที่ต้องการ
ผมไม่ได้รู้สึกว่าเอกสารของ Raku “แย่มากจริง ๆ” ตรงกันข้าม เว็บไซต์เอกสารทางการค่อนข้างน่าประทับใจ เพราะเป็น แหล่งข้อมูลครบจบในที่เดียว ที่มีทั้งเอกสารแนวคิดและเอกสาร API
https://docs.raku.org/
หน้านี้ยอดเยี่ยมมากในฐานะจุดเริ่มต้นของเอกสารแนวคิด: https://docs.raku.org/language
เนื้อหาส่วนใหญ่ที่มีอยู่เขียนดีและมีโค้ดตัวอย่างที่มีประโยชน์ แต่บางครั้งก็เจอส่วนที่ไม่ได้ทำเอกสารไว้เลย หรือครอบคลุมแค่กรณีง่าย ๆ โดยเฉพาะระบบโมดูลเป็นปัญหาที่สุด และความแตกต่างระหว่างโมดูล/แพ็กเกจนั้นเข้าใจได้ยากถ้าอ่านแค่หน้า Modules namespace ที่นำเข้ากับ namespace ที่ประกาศอาจต่างกันได้ แต่เพื่อให้คอมไพเลอร์หาเจอ โครงสร้างไดเรกทอรีต้องตรงกับ namespace จุดนี้มีประโยชน์และฟังดูสมเหตุสมผลแบบแปลก ๆ แต่ผมต้องลองจับเองถึงจะเข้าใจ
ตัวดำเนินการ Unicode เท่ ๆ ของ Raku ทั้งหมดมี รูปเขียนทดแทนแบบ ASCII
ตัวอย่างเช่น รูปเขียนทดแทนของ
∈,∉,∋,∌คือ(elem),!(elem),(cont),!(cont)https://docs.raku.org/language/unicode_ascii#Other_acceptabl...
ดูเหมือนว่าคำวิจารณ์แบบเดิม ๆ ที่มักออกมาก่อนจะเข้าใจภาษาอย่างดี ยังคงถูกพูดซ้ำอยู่ เมื่อก่อนคือเรื่อง “เสียงรบกวนบนบรรทัด” ของ Perl ตอนนี้ก็เป็นทำนองว่า Raku ใช้ โอเปอเรเตอร์ Unicode อย่างไม่ลังเล
แต่เรื่องนี้เป็นตัวเลือก และผมก็ลองใช้เองเพื่อให้โค้ดบนหน้าจอดูกะทัดรัดและสื่อความได้มากขึ้น เหมาะมากกับการใช้ Unicode อย่างรอบคอบและสร้างสรรค์ ปฏิกิริยาที่ไม่ชอบ sigil ก็พบได้บ่อย แต่ผมชอบพลังการแสดงออกแบบเลื่อยไฟฟ้าสารพัดประโยชน์ของ Perl เลยใช้ Raku/Perl 6 มาโดยตลอด และ Raku ให้ความรู้สึกเหมือนเอาความเป็น Perl ของ Perl มายกกำลังสอง มันเป็นระเบียบ สื่อความได้ดี และมีกองฟีเจอร์ขนาดมหึมาวางทับอยู่บน Perl ดี ๆ แบบเก่า เอกสารก็ดี แต่ยังต้องปรับปรุงต่อเนื่อง และถ้าเทียบกับเอกสารของ Perl มาตรฐานนั้นสูงมาก
การทำแบบนี้เพื่อประหยัดตัวอักษรไม่กี่ตัวดูซับซ้อนและไม่ค่อยมีความหมายเท่าไร
บางครั้งก็เคยสงสัยว่าภาษาโปรแกรมที่อัดแน่นด้วย syntax sugar จะหน้าตาเป็นอย่างไร ตอนนี้รู้แล้ว
ให้ความรู้สึกประมาณว่า “น่ากลัว แต่ก็ดึงดูดอย่างประหลาด ละสายตาไม่ได้ ขออีก”
ในคำแนะนำบน GitHub เขียนไว้ว่า “slaps roof of [programming language]* this bad boy can fit so much [syntax sugar] into it.” และใน Advent of Code ครั้งล่าสุด เขาก็ใช้ภาษานี้ชนะด้วย: https://github.com/betaveros/noulith
ควรจำไว้ว่าเดิมที Raku เริ่มต้นเป็น Perl 6 และปรัชญาการออกแบบจำนวนมากก็มาจากวิธีคิดแบบ Perl
จากการที่ผู้เขียนสะดุดกับโอเปอเรเตอร์
xสำหรับทำซ้ำสตริงทันที ดูเหมือนจะไม่ค่อยรู้ประวัติของ Perl และ Raku เพราะใน Perl เป็นแบบนั้นมาหลายสิบปีแล้วมันไม่ได้ถูกเรียกว่า Perl 6 โดยไม่มีเหตุผล และทีม Perl แทบจะทีมเดิมเป็นคนพัฒนา ถ้าเคยใช้ Perl 5 มาพอสมควร มรดกใน Perl6/Raku จะเห็นได้ชัดเจน โมเดลอ็อบเจ็กต์ทั้งหมดของ Raku ก็ใกล้เคียงกับ Moose.pm ซึ่งเป็นโมดูล Perl 5 บน CPAN ในเวอร์ชันที่ทรงพลังกว่าเล็กน้อย
พูดตามตรง ไวยากรณ์ regex ของ Perl 5/PCRE นั้นแย่มาก
เหตุผลที่มันมีอยู่ก็เพียงเพราะในไวยากรณ์ regex แบบเก่า
(?เป็นข้อผิดพลาดทางไวยากรณ์ จึงสามารถนำไปนิยามใหม่ให้หมายถึงอะไรก็ได้ Raku คือความพยายามที่จะออกแบบภาษา regex ที่สมเหตุสมผลตั้งแต่ต้น ในยุคที่เรารู้แล้วว่า regex ควรสื่ออะไร ทางเลือกอีกทางคือการติดอยู่กับของแบบ(?:this|(?>or that))ไปอีก 30 ปีผมไม่ได้แตะ Perl มานานแล้ว แต่ก็ยังได้ใช้ regex บ่อย ๆ
ในความหมายหนึ่ง มันเป็น เกรมลิน อย่างชัดเจน ผมชอบเครื่องมือที่แปลก ซับซ้อน แต่ช่วยเพิ่มผลิตภาพ
แต่ไม่เห็นด้วยกับการเปรียบเทียบ “โปรแกรมใหญ่ vs โปรแกรมเล็ก” คนที่ไม่รอบคอบอาจตีความว่านั่นหมายถึงมันเป็นภาษาที่ไม่ดีสำหรับงานใหญ่ แต่จริง ๆ แล้วมันอาจดีเท่าภาษาอื่นหรือดีกว่าด้วยซ้ำ ปัญหาคือเหมือนภาษาเกรมลินอื่น ๆ การใช้ให้ดีต้องอาศัยปัญญา ตัวอย่างเช่น คนที่สับสนระหว่าง
$xกับ@xแทบไม่มีเลยหากเคยใช้ภาษาคล้าย ๆ กันมาพอสมควร sigil ทำให้ตอนอ่านโค้ดรู้ชนิดอย่างง่ายของตัวแปรทันที หรือใน Raku ก็คืออินเทอร์เฟซทันที จึงกลับทำให้อ่านง่ายขึ้น และยังสามารถใช้เนมสเปซชื่อตัวแปรเดียวกันกับ sigil ต่างกันได้อย่างมีประโยชน์ มันดูแปลก ดูเหมือนตัวอักษรที่ไม่จำเป็น และต้องรู้ความหมาย แต่ก็ทำให้ชีวิตง่ายขึ้นได้: https://www.perl.com/article/on-sigils/ปัญหาเกิดกับคนที่ไม่ค่อยรู้ว่าตัวเองกำลังทำอะไร สำหรับคนเหล่านั้น ภาษาเกรมลินนี้อาจเป็นฝันร้ายที่มีชีวิต และต้องมีอุปกรณ์ป้องกันมากมาย เช่น ราวกันตกของเลนโบว์ลิง ห่วงยางเล่นน้ำ ถุงมือเคฟลาร์ หมวกนิรภัย และ GPS ไม่ได้แปลว่าสร้างตึกระฟ้าด้วยภาษาเกรมลินไม่ได้ เพียงแต่พวกไม่ใช่เกรมลินที่ชอบก่อเรื่องสร้างไม่ได้ ส่วนเกรมลินที่ฉลาดสร้างได้
เครื่องมือมีอยู่เพื่อช่วยให้ทีมทำสิ่งที่จำเป็นได้ดีขึ้นหรือเร็วขึ้น ถ้าใช้เครื่องมือที่ซับซ้อนเกินจำเป็นเพื่อคัดกรองจูเนียร์หรือเติมอัตตาของตัวเอง เครื่องมือนั้นก็ใกล้เคียงอาวุธมากกว่าความช่วยเหลือสำหรับคนอื่น ๆ ในองค์กร