Cognition: ภาษา antisyntax ใหม่ที่นิยามเมตาโปรแกรมมิงใหม่
(ret2pop.nullring.xyz)- Cognition เป็นโครงการวิจัยภาษาโปรแกรมที่เลือกใช้ antisyntax แบบ postfix เต็มรูปแบบ เพื่อหลีกเลี่ยงปัญหา read-ahead ของ Lisp และภาษา concatenative
- กลไกหลักอย่าง delimiter, ignore, singlet, falias, crank, metacrank ทำให้โปรแกรมเปลี่ยนกฎการ tokenize และรอบการประมวลผลของตัวเองได้
- การบูตสแตรปเริ่มจากสถานะที่อ่านทุกอักขระเป็น token เดี่ยว แล้วค่อยย้ายตัวเองไปสู่สภาพแวดล้อมที่ใช้ช่องว่างและขึ้นบรรทัดใหม่เป็น delimiter
crankและmetacrankควบคุมว่าจะประเมิน token เมื่อใด และจะสะสมไว้เมื่อใด ทำให้สามารถนิยาม ไวยากรณ์แบบ prefix เช่นคอมเมนต์#, escape\, quote[, macro(ภายในระบบ postfix ได้- dialect ของ Brainfuck ก็ไม่ได้ใช้ parser แยกต่างหาก แต่ implement ด้วย คำของ Cognition และกฎ tokenization แสดงทิศทางที่ทำให้ตัวไวยากรณ์เองกลายเป็นโค้ดและทำให้เป็นอัตโนมัติได้
จุดที่ Cognition มองว่าเป็นปัญหาในไวยากรณ์เดิม
- Lisp ให้เมตาโปรแกรมมิงที่ทรงพลังผ่าน s-expression และระบบ macro แต่ยังคงได้รับอิทธิพลจาก ไวยากรณ์ตายตัว
- วงเล็บเปิดเป็นสัญญาณว่าต้องอ่านต่อไปจนกว่าจะเจอวงเล็บปิด จึงยาก หรือในบาง implementation เป็นไปไม่ได้ ที่จะเปลี่ยนบทบาทของวงเล็บจากภายในภาษาเอง
- หากต้องการเปลี่ยนวิธีแบ่ง token ที่อ่านไปแล้วในภายหลัง จะต้องประมวลผลสตริงจำนวนมาก
- กระบวนการที่ต้องดู input ปัจจุบันแล้วอ่านสิ่งถัดไปเพิ่มคือ syntax และทันทีที่สมมติว่าต้อง read-ahead เป็นพื้นฐาน ก็จะถูกผูกกับรูปแบบไวยากรณ์บางอย่าง
- Cognition ใช้ antisyntax แบบ postfix เต็มรูปแบบเพื่อหลีกเลี่ยงสิ่งนี้
- คล้ายภาษา concatenative แต่ผู้พัฒนามองว่าภาษา concatenative ทั่วไปก็ยังมีปัญหา read-ahead เพราะวงเล็บเหลี่ยมหรืออักขระ quote ของสตริง
- ระบบ macro ของ Racket ถูกแยกออกว่าเป็นแนวทางที่ใช้ preprocessing ไม่ใช่การเปลี่ยนไวยากรณ์แบบ dynamic ระหว่างรัน
โปรเจกต์และแนวคิดพื้นฐาน
- Cognition เป็น โครงการวิจัยที่ยังดำเนินอยู่ ซึ่งทำร่วมกับ Matthew Hinton เป็นเวลาหลายเดือน
- repository ของ implementation อยู่ที่ cognition-rust และใน repository ยังมี paper เกี่ยวกับภาษานี้ด้วย
- การมีพื้นฐานเรื่อง parsing, tokenization และไวยากรณ์จะช่วยให้เข้าใจได้ง่ายขึ้น
- คำอธิบายเริ่มจากโค้ด “baremetal cognition” แล้วไล่ตามกระบวนการพัฒนาไปเป็นไวยากรณ์ที่คล้ายกับ Stem
Baremetal Cognition และ tokenization
- baremetal Cognition ดูเผิน ๆ คล้าย Brainfuck แต่ทำเมตาโปรแกรมมิงได้ทรงพลังกว่า
- โค้ดบูตสแตรปขนาดเล็กมากใช้งานแม้กระทั่งช่องว่างและการขึ้นบรรทัดใหม่ให้มีความหมาย และในสถานะเริ่มต้น อักขระเดี่ยวทุกตัวถูกอ่านเป็น token
- โดยพื้นฐาน Cognition ออกแบบเป็น stack-based แต่ใช้คำว่า container ในความหมายที่ทั่วไปกว่า stack
- ในสภาพแวดล้อมพื้นฐาน ไม่มีคำใดถูก execute อัตโนมัติ ยกเว้น falias พิเศษบางตัว
-
delimiter, ignore, singlet
- delimiter ช่วยให้ tokenizer รู้จุดสิ้นสุดของ token หนึ่งและจุดเริ่มต้นของ token ถัดไป
- รายการ delimiter แบบอักขระเดี่ยวสามารถแก้ไขและอ่านได้จากภายในโค้ด Cognition
- อักขระ ignore จะถูกข้ามในขั้นเริ่มเก็บ token ของแต่ละ read-eval-print loop
- ค่าเริ่มต้นคือทุกอักขระเป็น delimiter และไม่มีอักขระ ignore
- รายการ delimiter, singlet, ignore สามารถเปลี่ยนพฤติกรรมได้ด้วย flag แบบ whitelist/blacklist
- ค่าเริ่มต้นคือไม่มี delimiter แบบ blacklist, ไม่มี singlet แบบ whitelisted และไม่มีอักขระ ignore แบบ whitelisted
- singlet เป็นหมวด tokenization ที่สาม ซึ่งรวมตัวมันเองเข้าใน token แล้วจบการเก็บ token
-
falias
- falias คือรายการคำที่จะถูก execute ทันทีเมื่อถูกวางลงใน container
fซึ่งเป็น falias พื้นฐานจะไม่ถูกสะสมใน container แต่จะ execute คำบนสุดของ container แทน- ในตัวอย่าง เมื่อ
fexecuted,dจะเปลี่ยนรายการ delimiter ให้เป็นค่าสตริงของคำ - หลังจากนั้นการบูตสแตรปจะค่อย ๆ ทำให้อักขระอย่าง
l,g,t,dกลายเป็น non-delimiter และเปลี่ยนช่องว่างกับขึ้นบรรทัดใหม่ให้เป็น delimiter และ ignore
สภาพแวดล้อมการ execute ที่การบูตสแตรปสร้างขึ้น
- การบูตสแตรปช่วงแรกเปลี่ยนกฎ delimiter และ ignore จนสุดท้ายสร้างสภาพแวดล้อมที่ ช่องว่างและการขึ้นบรรทัดใหม่ เป็น token delimiter และช่องว่างกับการขึ้นบรรทัดใหม่ถูกละเว้นในขั้นเริ่มต้นของ token
- จากนั้นอ่าน
1และcrankแล้วfexecutecrankเพื่อเข้าสู่สภาพแวดล้อมแบบ crank 1 - หัวใจของกระบวนการนี้คือสามารถเปลี่ยนวิธี tokenization ระหว่างรันได้
- สามารถทำการเปลี่ยน delimiter, singlet, ignore ให้เป็นอัตโนมัติด้วยโปรแกรมได้
- เพราะเป็นแบบ postfix และไม่มี read-ahead จึงไม่จำเป็นต้อง parse token ล่วงหน้าหนึ่งตัวหรือมากกว่านั้นก่อนประเมิน expression
- falias ช่วยให้ execute คำบางคำได้โดยไม่ต้องมีคำแบบ prefix หรือการ execute คำพื้นฐานโดยอัตโนมัติ
crank และ metacrank
- crank เป็นกลไกที่กำหนดว่าจะ execute token บน stack ตามรอบใด
- คำว่า
crankรับตัวเลขเป็น argument แล้วหลังจากนั้นจะ execute stack top ทุก ๆ คำลำดับที่nที่ถูกวางลงใน container1 crankตอนท้ายของการบูตสแตรปสร้างสภาพแวดล้อมที่ประเมินทุก token- ในสถานะ
5 crankคำต่าง ๆ สามารถถูกสะสมไว้ได้จนกว่าจะมี token ตัวที่ห้าเข้ามา
- โค้ดตัวอย่างใช้
unglue,swap,quote,prepose,defเพื่อสร้างคำชื่อ2crankunglueดึงค่าของคำ และสามารถดึง function pointer ของ builtin อย่างcrankได้ด้วยpreposeคล้ายcomposeของ Stem แต่ใส่ไว้ด้านหน้าและใส่ลงใน VMACROdefนิยามให้2crankวาง2ลงบน stack แล้วเรียก builtincrank
-
container และ macro ของ Cognition
- ใน Stem สามารถวางคำลงบน stack ได้โดยตรง แต่ใน Cognition คำที่ยังไม่ถูกประเมินจะอยู่ใน container
- การออกแบบนี้ทำให้คำอย่าง
composeจัดการ container ที่มีคำเดี่ยวและ container อื่น ๆ ผ่าน API ที่สอดคล้องกันได้ - macro ของ Cognition ต่างจาก quote ของ Stem
- เมื่อ macro ถูกประเมิน องค์ประกอบทั้งหมดภายในจะถูกประเมินโดยไม่สนใจ crank
- เมื่อประเมิน macro ที่ bind กับคำ macro ทั้งหมดจะรันโดยไม่ขึ้นกับ crank และ cranker จะเพิ่มขึ้นเพียงครั้งเดียว
- macro มีประโยชน์สำหรับโค้ดที่เป็นอิสระจาก crank และ expansion เพื่อการ optimize
-
metacrank
n m metacrankตั้งค่าการประเมินด้วยคาบmให้กับองค์ประกอบที่อยู่ลึกลงไปnตัวจาก stackcrankเทียบเท่ากับ0 m metacrank- สำหรับ token หนึ่งตัว จะมี metacrank ที่ถูกประเมินได้เพียงตัวเดียว และ metacrank ที่ต่ำกว่าจะมีสิทธิ์ก่อน
- metacrank และ crank ใช้กับทั้ง token ในไฟล์และกระบวนการประเมินนิยามคำแบบ recursive
- metacrank ทำให้สามารถ จัดการไวยากรณ์โดยตรง เช่น “หลังจากอ่าน token n ตัวแล้ว ฉันอยาก execute token นี้”
- หลังจากโปรแกรมคำแบบ prefix แล้ว หากไม่ต้องการใช้ต่อก็
undefได้ - ยังสร้างอักขระ prefix ที่หยุดหลังจากจำนวน token ที่กำหนด แทนที่จะหยุดด้วยอักขระจบบางตัวได้ด้วย
- สามารถใส่ input ของผู้ใช้เข้าในโปรแกรมคณิตศาสตร์ แล้วส่ง output ของมันต่อให้ระบบไวยากรณ์อย่าง metacrank ได้
กระบวนการพัฒนาเป็น dialect ของ Stem
- หลังบูตสแตรป Cognition ค่อย ๆ สร้างไวยากรณ์ที่ใกล้กับ Stem v2 ภายในภาษาเอง
- ขั้นแรกนำ
fออกจากรายการ falias และเหลือไว้แค่ing- เพราะถ้าวาง
fลงบน stack โดยตรงมันจะถูก execute จึงสร้างffแล้วตัดสตริงครึ่งหนึ่งเพื่อให้ได้fสองตัว - หลังจากนั้น
fถูกนิยามเป็นคำว่างที่แทน false
- เพราะถ้าวาง
-
คอมเมนต์
#- อักขระ
#เป็นตัวอย่างโค้ดแรกที่ทำงานเหมือน prefix จริง ๆ - อักขระคอมเมนต์นี้ทำงานเป็น prefix ที่ทิ้งข้อความจนถึงก่อน newline ทำให้เกิดไวยากรณ์แบบที่ parser อ่านข้างหน้า
- implementation ผสมผสาน
geti,getd,gets,crankbase,halt,VMACRO cast,singlet,delimเป็นต้น geti,getd,getsดึง ignore, delimiter, singlet ออกมาเป็นสตริงตามลำดับhaltทำให้ metacrank ทั้งหมดเป็น 0VMACRO castเปลี่ยน container บนสุดของ stack ให้เป็น macro- นิยามของ
#จะเปลี่ยนกฎ tokenization แล้วเรียก#กับคำที่จะถูก tokenize ในอนาคต จากนั้น drop คอมเมนต์นั้น แล้วกลับไปยัง crank และ metacrank เดิม
- อักขระ
-
escape, quote, macro
\ถูกนิยามเป็น อักขระ escape ที่ช่วยให้วางคำลงบน stack ได้แม้เป็นคำที่จะถูกประเมิน- จากนั้นเพิ่มนิยาม quote
[แล้วใช้นิยาม quote นั้นอีกครั้งเพื่อนิยาม[ใหม่ให้ดีกว่าเดิมและ quote แบบ recursive ได้ - เพราะ
defเป็น postfix จึงสามารถใช้คำจำกัดความเดิมมาสร้างคำจำกัดความใหม่ได้ - pattern นี้เป็นวิธีพัฒนาที่พบบ่อยใน Cognition ระดับ low-level
(ถูกนิยามเป็นวงเล็บ macro- macro ถูกทำให้ขยายโดยอัตโนมัติ โดยมองว่าการ bind macro ที่ขยายแล้วเข้ากับคำจะมีประสิทธิภาพกว่า
- ในเชิงฟังก์ชันจะถูกประเมินด้วยวิธีเดียวกัน
expandขยายคำจำกัดความของคำใน quote หรือ macro แบบ recursive ด้วยunglue- เริ่มจากนิยาม
expandพื้นฐานก่อน แล้วใช้งานexpandเองเพื่อนิยามใหม่ให้รองรับกรณีทั่วไปมากขึ้น
dialect ของ Brainfuck
- Cognition นิยาม dialect ของ Brainfuck บน dialect ของ Stem ที่พัฒนาแล้ว
- ตัวอย่างการรันคือ
../crank -s 2 bootstrap.cog helloworld.bf brainfuck.cog brainfuck.cogไม่ใช่ parser ของ Brainfuck ในความหมายทั่วไป- มันนิยามคำของ Brainfuck
- tokenize Brainfuck
- execute ในสภาพแวดล้อม native ของ Cognition
- ตัวอย่างนี้แสดงให้เห็นว่าสามารถนิยามไวยากรณ์ทางเลือกด้วยไวยากรณ์ของ Cognition ได้ง่าย
- แทนที่จะอ่านสัญลักษณ์แล้วตัดสินใจพฤติกรรมตามสัญลักษณ์ Cognition นิยาม อักขระ prefix เองเป็นคำ ที่ใช้ metacrank แล้วใส่ไวยากรณ์ไว้ในนิยามคำ
แนวคิด dialect dialect
- สามารถจินตนาการถึงคำอย่าง
mkprefixได้- เช่น คำที่รับ input สองคำคือ
[และ]พร้อมกับ operation บางอย่าง แล้วนิยามให้[ใช้ operation นั้นไปเรื่อย ๆ จนกว่าจะพบ]โดยอัตโนมัติ
- เช่น คำที่รับ input สองคำคือ
- แนวคิดนี้เป็นไปได้เพราะทั้ง
metacrankและdefเป็น คำทั่วไป - เนื่องจากทุกอย่าง รวมถึง
d,i,sล้วนเป็นคำ จึงสามารถสร้าง dialect ที่เป็นนามธรรมยิ่งขึ้นเพื่อทำให้กระบวนการ implement ไวยากรณ์เป็นอัตโนมัติได้ - ยังไม่มี implementation ใน standard library แต่มีรายการที่เคยหารือกับ Matthew Hinton ในฐานะความเป็นไปได้ของ standard library
- metaword ที่สร้างและเรียกคำเชิงนามธรรมโดยอัตโนมัติ
- การค้นหา word-generator ที่จะ abstract wordlist ปัจจุบันโดยอัตโนมัติ
- วิธีสั่ง framework abstraction เพื่อแก้ปัญหา
ความเป็นไปได้ของการจัดการไวยากรณ์เป็นโค้ด
- ใน Cognition การประมวลผลสตริงเทียบได้กับ post-processing ของ tokenizer ทำให้ operation บนสตริงมีความหมายที่ทรงพลัง
- มีการเสนอพื้นที่ประยุกต์ที่เป็นไปได้ เช่น Symbolic AI, การวิจัย syntax และ grammar, การทดลอง prototype ของภาษาและ metalanguage
- ยังมีไอเดียอย่างโปรแกรมอ่านไฟล์ config, shell บน Cognition, ระบบปฏิบัติการบน Cognition
- แก่นสำคัญคือ Cognition ทำให้ “syntax as code” เป็นไปได้
- สามารถโปรแกรมไวยากรณ์แบบ dynamic ได้
- สามารถทำให้การสร้างไวยากรณ์เองเป็นอัตโนมัติได้
- แนวคิดอย่าง Metastack และ
cdยังไม่ได้กล่าวถึง และยังเป็นหัวข้อที่อาจนำไปพูดถึงในบทความต่อไป
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ตัวอย่างเช่น ใน Racket คุณสามารถสร้าง implementation ของ Datalog แบบ built-in ที่ใช้ไวยากรณ์ Datalog และยังทำงานร่วมกับโมดูล Racket อื่น ๆ ได้ โดยไม่เปลี่ยน data model พื้นฐาน
มันเป็นวิธีทำ metaprogramming โดยไม่ติดอยู่กับ S-expression และจัดการในระดับที่สูงกว่า
การ bootstrap ไวยากรณ์ แบบนี้ดูเท่มากและมีคุณค่าในเชิงงานวิจัย แต่ไม่แน่ใจว่าดีกว่าแนวทางของ Racket อย่างเป็นพื้นฐานหรือไม่
macro ของ Lisp, Scheme, Racket มักทำงานบน AST แต่ Rhombus ทำงานบน "shrubbery" ซึ่งคล้าย AST แต่เลื่อนการตัดสินใจในการ parse บางส่วนออกไปภายหลัง ทำให้มีความยืดหยุ่นในการขยายไวยากรณ์เพิ่มขึ้นเล็กน้อย
อ้างอิง: https://docs.racket-lang.org/guide/hash-reader.html, https://docs.racket-lang.org/datalog/datalog.html, paper ของ Rhombus https://doi.org/10.1145/3580417
#langของ Racket ใช้ง่ายกว่า readtable ของ CLแค่ readtable อย่างเดียวก็ทรงพลังพอจะใช้สร้าง C compiler ได้แล้ว: https://github.com/vsedach/Vacietis
ส่วนตัวผมหัวเราะลั่นตรงที่มีคำว่า "metacrank"
ใน Emacs Lisp, Common Lisp และ ISLISP macro ก็แค่รับข้อมูลบางอย่างแล้วคืนข้อมูลบางอย่าง ไม่มีแนวคิดอย่าง AST อยู่ตรงนั้น
เวลาเรียก
(foo-macro ...)นั้น...อาจเป็นข้อมูลอะไรก็ได้ตัวอย่างเช่น
(defmacro rev (&rest items) (reverse items))ก็แค่รับรายการอาร์กิวเมนต์ต้นฉบับของการเรียก macro แล้วสลับลำดับมันมันสามารถใช้แบบ
(rev 1 2 3 4 +)หรือ(rev (rev 10 n -) (+ a 20 b) (rev 30 a *) list)และสิ่งที่ถูกส่งเข้าไปจริง ๆ ก็คือ list, number และ symbolมันไม่ใช่ข้อความและไม่ใช่ AST และถ้าส่งข้อมูลที่คำนวณแล้วเข้า
evalมันก็ทำงานแบบเดียวกันreader ของ Lisp โดยพื้นฐานอ่าน s-expression ซึ่งเป็นชั้นข้อมูล ส่วน EVAL, macro และความสามารถอื่น ๆ ก็รับข้อมูลเป็นหลัก
compiler อาจสร้างตัวแทน AST ภายในก็ได้ แต่นั่นเป็นอิสระของ implementation และภาษา Lisp โดยทั่วไปนิยามอยู่บนไวยากรณ์ของข้อมูล ไม่ใช่ไวยากรณ์ของข้อความ
Lisp interpreter เป็น "List Processor" ที่ประมวลผล S-expression ระหว่างรัน ไม่ใช่ข้อความ และ
COMPILEก็รับ S-expression ไม่ใช่ข้อความเช่นกันRacket และ Scheme มีระบบ macro แยกต่างหาก
กว่าจะพูดถึงโปรเจ็กต์จริงอย่าง Cognition ก็ล่วงไปเกิน 300 คำแล้ว และแม้เรื่อง Lisp จะน่าสนใจ แต่ก็ไม่แน่ใจว่านั่นคือส่วนที่สำคัญที่สุดของโปรเจ็กต์หรือไม่
เวลาอ่านบทความเชิงข้อมูล เราจะคอยประเมินตลอดว่า "คุ้มไหมที่จะใช้เวลาอ่าน?" ดังนั้นควรบอกตั้งแต่ต้นว่าเอกสารนี้เกี่ยวกับอะไร
แค่เปิดด้วยประโยคอย่าง "Cognition คือภาษาใหม่ที่สำรวจไวยากรณ์ที่ผู้ใช้แก้ไขได้" ก็น่าจะพอ แต่แม้อ่านเลยสี่พารากราฟแรกไปแล้ว ก็ยังตัดสินยากว่าคุ้มจะอ่านต่อไหม
ถ้าเวลาคือเงิน เวลาที่ใช้กับบทความนี้ก็นับว่าสูญเปล่า
แทนที่จะคาดหวังให้ทุกคอนเทนต์บนอินเทอร์เน็ตปรับให้ตรงกับรสนิยมส่วนตัว ผมคิดว่าการปรับตัวเข้ากับรูปแบบที่มีอยู่แล้วดีกว่า
ข้อความไม่ใช่สื่อที่ต้องเสพแบบเรียงลำดับอย่างเดียวเหมือนวิดีโอ คุณสแกนผ่าน ๆ หาเรื่องที่สนใจก่อนได้ ถ้าไม่มีก็ทิ้งไป ถ้ามีก็ค่อยย้อนกลับไปอ่านตั้งแต่ต้น
รูปแบบการเขียนที่หลากหลายดีกว่า เพราะทำให้เราคัดกรองข้อมูลที่เสพอย่างมีสติ และถ้าเสพแบบรับอย่างเดียว หัวก็จะขี้เกียจ
แต่ถ้าเป็นวิดีโอผมคงเห็นด้วย
วิดีโอต้องตัดสินใจก่อนดูว่าจะคุ้มเวลาหรือไม่ และแม้การดูที่ 2x หรือการข้ามทีละ 5-10 วินาทีจะช่วยได้บ้าง แต่ก็ไม่ได้แก้ปัญหา
เริ่มจากอธิบายปัญหา แล้วค่อยเสนอวิธีแก้
แค่อ่านไม่กี่ประโยคก็พอจะรู้แล้วว่านี่คือคำตอบแบบดอนกิโฆเต้ต่อ "ปัญหา" ที่คน 99.999% ไม่สนใจ รวมถึงคนอย่างผมที่เคยได้ยิน Lisp แต่ไม่เคยใช้นอกไฟล์ตั้งค่า Emacs ถึงอย่างนั้นผมก็ยังอ่านต่อ
ถ้าไม่มีช่วงนี้ เนื้อหาหลังจากนั้นน่าจะเข้าใจยากกว่านี้
ถ้าไม่รู้ฉากหลังนั้น ก็อาจพลาดบริบทของทั้งบทความ และยังตัดสินได้ยากด้วยว่านี่เป็นการโต้แย้งหุ่นฟางหรือเปล่า
เพราะงั้นมันเลยให้ความรู้สึกเหมือนทั้งหมดนี้มีอยู่เพื่อความต้องการที่เฉพาะทางมาก ๆ ขณะที่ชื่อเรื่องกลับดูทั่วไปกว่าและเหมือนเป็นแนวคิดที่น่าสนใจทีเดียว
ภายในสองประโยคแรกก็เผยให้เห็นแล้วว่ากำลังจะแก้ ปัญหา อะไร ซึ่งมีประโยชน์กว่าบทนำที่เสนอมาเสียอีกสำหรับการประเมินว่าผมสนใจไหม
แต่สำหรับผม เวลามองขึ้นไปตามบันไดแห่งความบริสุทธิ์ของการเขียนโปรแกรม ระดับ Forth ก็คือขีดจำกัดของความบริสุทธิ์เชิงปรัชญาที่ผมยังพอรับไหว
ต่อจากนี้ก็จะยังทำเวทมนตร์มืดอีกมากต่อไป
เพียงแต่เกือบทุกภาษา รวมถึง Lisp ทุกตัว จัดการเรื่อง quotation ผิดพลาด และที่ตลกคือ
m4กลับเป็นข้อยกเว้นLisp หลบปัญหานี้ด้วยแมโคร ทำให้จัดการประโยคของภาษาเมตาที่เขียนด้วยประโยคของภาษาอ็อบเจ็กต์ได้โดยไม่ต้องสนใจการ quote
ปัญหานี้เกิดจากการที่ทั้งภาษาอ็อบเจ็กต์และภาษาเมตาถือว่าช่องว่างเป็นจุดจบของอะตอม แต่ไม่ได้แยกสองสิ่งนี้ออกจากกัน
แนวทางของ Cognition ที่เป็น anti-syntax แบบ postfix สมบูรณ์นั้นคล้ายกับภาษาโปรแกรมเชิง concatenative แต่ ภาษา postfix เป็นคู่ตรงข้ามของภาษา prefix และเจอปัญหาเดียวกัน
ต้องกำหนด arity ของทุกสัญลักษณ์ล่วงหน้าและไม่ใช้ higher-order function หรือไม่ก็ต้องมีตัวคั่นหนึ่งคู่ที่ใช้ serialize ต้นไม้ได้
การพึ่งพา implicit stack แบบปริยายก็คล้ายกับการทำ lobotomy ที่กลีบหน้าผากเพื่อรักษาโรคซึมเศร้า
พวกเราก็ยังไม่รู้เหมือนกันว่าสิ่งที่เราสร้างขึ้นใหม่นั้นใหม่แค่ไหน และถ้าคุณคิดว่าสามารถทำสิ่งที่เรากำลังทำด้วย Lisp ได้ ก็เชิญพิสูจน์ว่าเราคิดผิดได้
m4ต่างกันอย่างไรข้ออ้างนั้นน่าสนใจ แต่ต้องการรายละเอียดที่เป็นรูปธรรมมากกว่านี้
เช่นเดียวกับที่การทำ lobotomy ลดความสามารถในการประมวลผลระดับสูง การย้อนกลับไปสู่การคำนวณแบบสตริงคำสั่งที่ดิบที่สุดก็อาจมองว่าใกล้เคียงกันได้
https://www.hpmuseum.org/rpnvers.htm
โดยเฉพาะที่กลไกนั้นเล็กมาก
ฉันไม่ใช่ผู้เชี่ยวชาญด้านภาษาเลยไม่แน่ใจว่ามันใหม่จริงไหม แต่ตลอดเวลาที่อ่านบทความนี้รู้สึกได้ถึงความยินดีของผู้เขียนที่ทุกครั้งที่ข้ามเนินไปก็พบเทือกเขาแห่งความเป็นไปได้ใหม่ ๆ
ถ้าเข้าใจถูก เรื่องนี้หมายความว่าสามารถสร้าง เครื่องจักรที่คิดได้ จริง ๆ ด้วย Cognition
โปรแกรมไม่จำเป็นต้องหยุดแล้วค่อยเริ่มใหม่ด้วยคำสั่งใหม่ แต่สามารถเขียนและรัน subroutine ใหม่ด้วยตัวเองจากอินพุตใหม่ได้
กล่าวคือ โปรแกรมสามารถเรียนรู้และปรับตัวได้ด้วยการสร้าง abstraction ใหม่และเชื่อมเข้ากับ API ใหม่
สำหรับฉัน สิ่งนี้น่าสนใจกว่าโครงข่ายประสาทที่ใหญ่ขึ้นหรือเทคนิคการเรียนรู้แบบใหม่เสียอีก
Common Lisp มี reader macro ที่ให้เปลี่ยนไวยากรณ์ได้ตามต้องการ และยังมีคอมไพเลอร์ Fortran ที่อ่านไวยากรณ์ Fortran ผ่าน reader macro ด้วย
Common Lisp มีทั้ง reader macro ที่ทำงานตอนอ่าน, macro, และ compiler macro ที่ทำงานตอนคอมไพล์ และภาษาแมโครทั้งหมดนี้ก็คือ Common Lisp
metaprogramming ไม่ได้เกี่ยวข้องกับแมโครหรือไวยากรณ์มากนัก แต่หมายถึงความสามารถในการจัดการความหมายและ semantics ของ type, interface, class, method เป็นต้น
ถ้า CL เองยังไม่ทรงพอ ก็มี CLOS หรือ Common Lisp Metaobject Protocol สำหรับเรื่องนี้
ด้วย CL reader macro คุณใช้ tokenizer แบบอื่นได้ก็จริง แต่ต้องบอกการสลับ tokenizer ผ่าน expression ใน read table
ใน Cognition ดูเหมือนว่าเมื่อเรียกฟังก์ชันแล้ว tokenizer ของบริบทผู้เรียกจะเปลี่ยนไป
ที่เรียนมาว่างานแบบนี้สำคัญเมื่อทำกับเครื่องทัวริงหรือลัมบ์ดาแคลคูลัส ก็เพื่อแสดงว่าภาษาระดับสูงเทียบเท่ากับภาษาพื้นฐาน และดังนั้นสิ่งที่อนุมานได้กับอย่างแรกก็นำไปใช้กับอย่างหลังได้ด้วย
ตัวอย่างแรกและตัวอย่างเดียวที่นึกออกคือปัญหา halting
ในระดับที่ใช้งานจริง ถ้าพิสูจน์ได้ว่าภาษาพื้นฐานไม่มี memory leak จะพูดได้ไหมว่าภาษาที่สืบทอดจากมันก็ไม่มีการรั่วไหลเช่นกัน?
เลยสงสัยว่าการ bootstrap แบบนี้มีข้อดีอะไร
ถ้าคำตอบเป็นเพียงแบบปีนเอเวอเรสต์ว่า "เพราะมันอยู่ตรงนั้น" ก็เคารพได้เหมือนกัน
df, ช่องว่างในบรรทัด 3 และการขึ้นบรรทัดใหม่มีความสำคัญ ก็รู้สึกทันทีว่า "ขอบคุณแต่ขอผ่าน"อักขระช่องว่างสามตัวท้ายบรรทัดก่อนหน้าหมายถึงการประชด และถ้าอยู่ในที่ที่แยก trailing whitespace ได้ยาก ก็ให้ตีความตามตัวอักษรได้เลย
ส่วนที่พูดถึงการ bootstrap นั้นจริง ๆ คือช่วงที่กำลังบอกผู้อ่านให้ถือว่าช่องว่างกับการขึ้นบรรทัดใหม่เป็นตัวคั่น
พูดอีกอย่างคือกำลังบ่นว่าช่องว่างมีความหมายอยู่ในส่วนที่มันถูกประกาศให้เป็นตัวคั่น
แน่นอนว่าจะคิดแบบนั้นก็ได้ แต่ก็อดสงสัยไม่ได้ว่ามีวิธีทำสิ่งนี้ให้ดีกว่านี้ไหม
อย่างน้อยครั้งหนึ่งฉันก็นึกวิธีทำสิ่งนี้ไม่ออกเลย หากไม่ให้ช่องว่างตามตัวอักษรมีความหมายในลักษณะนั้น
คิดหรือว่าสามารถอ่านประโยคอย่าง "sentence this without you syntax read can" ได้โดยไม่มีไวยากรณ์?
แม้ Cognition จะใช้ anti-syntax แบบ postfix สมบูรณ์ แต่ postfix ก็ยังเป็นไวยากรณ์
ลองไปถามผู้พูดภาษาเยอรมันเรื่องกริยาที่ไปอยู่ท้ายประโยคดูก็ได้
แม้แต่ในตัวอย่างแรก ลำดับของ operand และ operator ก็ยังสำคัญ และนั่นแหละคือไวยากรณ์
สิ่งนี้ดูเหมือนความพยายามสร้างภาษาที่บีบอัดอย่างไร้เหตุผล และชวนให้นึกถึง APL มาก
ขอฝากคำใบ้ถึงผู้เขียนว่า คุณไม่ได้ลบไวยากรณ์ออกไป แค่ทำให้มนุษย์อ่านและเข้าใจได้ยากขึ้นเท่านั้น และ ความสามารถในการอ่านและความเข้าใจได้ เป็นองค์ประกอบสำคัญในการเขียนโปรแกรม
ให้ความรู้สึกเหมือนกฎใต้เท้าเปลี่ยนอยู่ตลอด ราวกับว่ากฎและคำต่าง ๆ ถูกแนะนำขึ้นมาแล้วก็นิยามใหม่ตามอำเภอใจ
โดยรวมแล้วมีกลิ่นอายแบบ Numberwang อยู่ ซึ่งดูเป็นหนึ่งในเหตุผลที่ทำให้คนรับมันเป็นงานเสียดสี
อีกเหตุผลใหญ่คือขั้นตอน bootstrap ถูกเขียนออกมาอย่างชวนขำเกินจริง ซึ่งก็ดูเหมือนตั้งใจไว้
เห็นได้ชัดว่ามีอะไรลึกซึ้งอยู่ แต่คงต้องไปดื่มกาแฟที่เข้มกว่านี้แล้วค่อยกลับมาอ่านใหม่
ฉันเป็นผู้เขียนบทความนี้เอง และปัญหาคือมีสิ่งที่ต้องสื่อสารเยอะมากจริง ๆ
ฉันกับ Matthew คุยกันวันละหลายชั่วโมงต่อเนื่องอยู่ 3 สัปดาห์เกี่ยวกับการออกแบบภาษานี้ และยังมีบริบทอีกมากที่ต้องเติมให้คนที่ไม่ได้รู้จักฉันเป็นการส่วนตัว