3 คะแนน โดย GN⁺ 2024-05-03 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • 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 แทน
    • ในตัวอย่าง เมื่อ f execute d, d จะเปลี่ยนรายการ delimiter ให้เป็นค่าสตริงของคำ
    • หลังจากนั้นการบูตสแตรปจะค่อย ๆ ทำให้อักขระอย่าง l, g, t, d กลายเป็น non-delimiter และเปลี่ยนช่องว่างกับขึ้นบรรทัดใหม่ให้เป็น delimiter และ ignore

สภาพแวดล้อมการ execute ที่การบูตสแตรปสร้างขึ้น

  • การบูตสแตรปช่วงแรกเปลี่ยนกฎ delimiter และ ignore จนสุดท้ายสร้างสภาพแวดล้อมที่ ช่องว่างและการขึ้นบรรทัดใหม่ เป็น token delimiter และช่องว่างกับการขึ้นบรรทัดใหม่ถูกละเว้นในขั้นเริ่มต้นของ token
  • จากนั้นอ่าน 1 และ crank แล้ว f execute crank เพื่อเข้าสู่สภาพแวดล้อมแบบ 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 ที่ถูกวางลงใน container
    • 1 crank ตอนท้ายของการบูตสแตรปสร้างสภาพแวดล้อมที่ประเมินทุก token
    • ในสถานะ 5 crank คำต่าง ๆ สามารถถูกสะสมไว้ได้จนกว่าจะมี token ตัวที่ห้าเข้ามา
  • โค้ดตัวอย่างใช้ unglue, swap, quote, prepose, def เพื่อสร้างคำชื่อ 2crank
    • unglue ดึงค่าของคำ และสามารถดึง function pointer ของ builtin อย่าง crank ได้ด้วย
    • prepose คล้าย compose ของ Stem แต่ใส่ไว้ด้านหน้าและใส่ลงใน VMACRO
    • def นิยามให้ 2crank วาง 2 ลงบน stack แล้วเรียก builtin crank
  • 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 ตัวจาก stack
    • crank เทียบเท่ากับ 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 ทั้งหมดเป็น 0
    • VMACRO 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 นั้นไปเรื่อย ๆ จนกว่าจะพบ ] โดยอัตโนมัติ
  • แนวคิดนี้เป็นไปได้เพราะทั้ง 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 ความคิดเห็น

 
GN⁺ 2024-05-03
ความคิดเห็นจาก Hacker News
  • ยังไม่ค่อยถูกโน้มน้าวว่าวิธีนี้ดีกว่า การตั้งค่าชั้น reader ของ Racket
    ตัวอย่างเช่น ใน 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
    • ก็ยังไม่มั่นใจเหมือนกันว่ามันดีกว่า readtable ของ Common Lisp และผมมองว่า #lang ของ Racket ใช้ง่ายกว่า readtable ของ CL
      แค่ readtable อย่างเดียวก็ทรงพลังพอจะใช้สร้าง C compiler ได้แล้ว: https://github.com/vsedach/Vacietis
    • พอเห็นว่าใช้ Brainfuck เป็นตัวอย่างหลัก ก็ไม่ค่อยแน่ใจว่าเขาต้องการให้เรารับมันอย่างจริงจังแค่ไหน
      ส่วนตัวผมหัวเราะลั่นตรงที่มีคำว่า "metacrank"
    • คำกล่าวที่ว่า macro ของ Lisp ทำงานบน AST นั้นไม่จริงสำหรับ Lisp
      ใน 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 ถึงอย่างนั้นผมก็ยังอ่านต่อ
    • ส่วนที่เกี่ยวกับ Lisp อาจไม่ใช่องค์ประกอบที่สำคัญที่สุดของโปรเจ็กต์ แต่ก็มีบทบาทชัดเจนในการแสดงให้เห็นว่าปัญหาที่โปรเจ็กต์นี้พยายามแก้คือปัญหาแบบไหน
      ถ้าไม่มีช่วงนี้ เนื้อหาหลังจากนั้นน่าจะเข้าใจยากกว่านี้
    • ผมสนใจแนวคิดนี้นะ แต่ประโยคแรกดูเหมือนพยายามอธิบายความจำเป็นโดยอิงจากปฏิกิริยาต่อ ไวยากรณ์ S-expression ของ Lisp เลยกังวลว่าจะหลุดบริบท
      ถ้าไม่รู้ฉากหลังนั้น ก็อาจพลาดบริบทของทั้งบทความ และยังตัดสินได้ยากด้วยว่านี่เป็นการโต้แย้งหุ่นฟางหรือเปล่า
      เพราะงั้นมันเลยให้ความรู้สึกเหมือนทั้งหมดนี้มีอยู่เพื่อความต้องการที่เฉพาะทางมาก ๆ ขณะที่ชื่อเรื่องกลับดูทั่วไปกว่าและเหมือนเป็นแนวคิดที่น่าสนใจทีเดียว
    • ผมว่าบทความในตอนนี้ก็โอเคสมบูรณ์แล้ว
      ภายในสองประโยคแรกก็เผยให้เห็นแล้วว่ากำลังจะแก้ ปัญหา อะไร ซึ่งมีประโยชน์กว่าบทนำที่เสนอมาเสียอีกสำหรับการประเมินว่าผมสนใจไหม
  • เป็นบทความที่น่าสนใจ และหวังว่าผู้เขียนจะไม่ใส่ใจกับความประชดประชันที่นี่ แล้วเดินหน้าทำ พิธีเวทมนตร์มืด ต่อไป
    แต่สำหรับผม เวลามองขึ้นไปตามบันไดแห่งความบริสุทธิ์ของการเขียนโปรแกรม ระดับ Forth ก็คือขีดจำกัดของความบริสุทธิ์เชิงปรัชญาที่ผมยังพอรับไหว
    • ในฐานะผู้เขียนบทความนี้ ผมไม่ได้ใส่ใจกับคำประชดเลย กลับรู้สึกว่ามันค่อนข้างขำและยินดีด้วยซ้ำ
      ต่อจากนี้ก็จะยังทำเวทมนตร์มืดอีกมากต่อไป
  • metaprogramming กับ programming คือสิ่งเดียวกัน
    เพียงแต่เกือบทุกภาษา รวมถึง Lisp ทุกตัว จัดการเรื่อง quotation ผิดพลาด และที่ตลกคือ m4 กลับเป็นข้อยกเว้น

Lisp หลบปัญหานี้ด้วยแมโคร ทำให้จัดการประโยคของภาษาเมตาที่เขียนด้วยประโยคของภาษาอ็อบเจ็กต์ได้โดยไม่ต้องสนใจการ quote
ปัญหานี้เกิดจากการที่ทั้งภาษาอ็อบเจ็กต์และภาษาเมตาถือว่าช่องว่างเป็นจุดจบของอะตอม แต่ไม่ได้แยกสองสิ่งนี้ออกจากกัน
แนวทางของ Cognition ที่เป็น anti-syntax แบบ postfix สมบูรณ์นั้นคล้ายกับภาษาโปรแกรมเชิง concatenative แต่ ภาษา postfix เป็นคู่ตรงข้ามของภาษา prefix และเจอปัญหาเดียวกัน
ต้องกำหนด arity ของทุกสัญลักษณ์ล่วงหน้าและไม่ใช้ higher-order function หรือไม่ก็ต้องมีตัวคั่นหนึ่งคู่ที่ใช้ serialize ต้นไม้ได้
การพึ่งพา implicit stack แบบปริยายก็คล้ายกับการทำ lobotomy ที่กลีบหน้าผากเพื่อรักษาโรคซึมเศร้า

  • ขอบคุณสำหรับข้อเสนอแนะ แต่ถ้ายังไม่ได้อ่านบทความทั้งหมด ก็ควรอ่านก่อน
    พวกเราก็ยังไม่รู้เหมือนกันว่าสิ่งที่เราสร้างขึ้นใหม่นั้นใหม่แค่ไหน และถ้าคุณคิดว่าสามารถทำสิ่งที่เรากำลังทำด้วย Lisp ได้ ก็เชิญพิสูจน์ว่าเราคิดผิดได้
  • อยากเห็นตัวอย่างว่าการ quote ใน Lisp กับ m4 ต่างกันอย่างไร
    ข้ออ้างนั้นน่าสนใจ แต่ต้องการรายละเอียดที่เป็นรูปธรรมมากกว่านี้
  • อุปมาเรื่อง implicit stack นั้นหวือหวาก็จริง แต่ implicit stack มีมาตั้งแต่ยุคคอมพิวเตอร์หรือเครื่องคิดเลขยุคแรกแล้ว
    เช่นเดียวกับที่การทำ lobotomy ลดความสามารถในการประมวลผลระดับสูง การย้อนกลับไปสู่การคำนวณแบบสตริงคำสั่งที่ดิบที่สุดก็อาจมองว่าใกล้เคียงกันได้
    https://www.hpmuseum.org/rpnvers.htm
  • ฉันคิดว่ามันงดงามมากที่โปรแกรม Cognition สามารถนิยามและนิยามโครงสร้างไวยากรณ์ใหม่ระหว่างรันไทม์ และเข้าออกจากมันได้
    โดยเฉพาะที่กลไกนั้นเล็กมาก
    ฉันไม่ใช่ผู้เชี่ยวชาญด้านภาษาเลยไม่แน่ใจว่ามันใหม่จริงไหม แต่ตลอดเวลาที่อ่านบทความนี้รู้สึกได้ถึงความยินดีของผู้เขียนที่ทุกครั้งที่ข้ามเนินไปก็พบเทือกเขาแห่งความเป็นไปได้ใหม่ ๆ
    ถ้าเข้าใจถูก เรื่องนี้หมายความว่าสามารถสร้าง เครื่องจักรที่คิดได้ จริง ๆ ด้วย 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 สำหรับเรื่องนี้
    • สิ่งที่พูดถึงตรงนี้คือ reader macro ของ CL
      ด้วย CL reader macro คุณใช้ tokenizer แบบอื่นได้ก็จริง แต่ต้องบอกการสลับ tokenizer ผ่าน expression ใน read table
      ใน Cognition ดูเหมือนว่าเมื่อเรียกฟังก์ชันแล้ว tokenizer ของบริบทผู้เรียกจะเปลี่ยนไป
  • ดูเป็นตัวอย่างเชิงปฏิบัติของการ bootstrap เครื่องจักรขั้นต่ำให้เป็นอินเทอร์พรีเตอร์ของภาษาระดับสูง
    ที่เรียนมาว่างานแบบนี้สำคัญเมื่อทำกับเครื่องทัวริงหรือลัมบ์ดาแคลคูลัส ก็เพื่อแสดงว่าภาษาระดับสูงเทียบเท่ากับภาษาพื้นฐาน และดังนั้นสิ่งที่อนุมานได้กับอย่างแรกก็นำไปใช้กับอย่างหลังได้ด้วย
    ตัวอย่างแรกและตัวอย่างเดียวที่นึกออกคือปัญหา halting
    ในระดับที่ใช้งานจริง ถ้าพิสูจน์ได้ว่าภาษาพื้นฐานไม่มี memory leak จะพูดได้ไหมว่าภาษาที่สืบทอดจากมันก็ไม่มีการรั่วไหลเช่นกัน?
    เลยสงสัยว่าการ bootstrap แบบนี้มีข้อดีอะไร
    ถ้าคำตอบเป็นเพียงแบบปีนเอเวอเรสต์ว่า "เพราะมันอยู่ตรงนั้น" ก็เคารพได้เหมือนกัน
  • พอถึงส่วนที่บอกว่าช่องว่างหลัง df, ช่องว่างในบรรทัด 3 และการขึ้นบรรทัดใหม่มีความสำคัญ ก็รู้สึกทันทีว่า "ขอบคุณแต่ขอผ่าน"
    อักขระช่องว่างสามตัวท้ายบรรทัดก่อนหน้าหมายถึงการประชด และถ้าอยู่ในที่ที่แยก trailing whitespace ได้ยาก ก็ให้ตีความตามตัวอักษรได้เลย
    • แก่นของการทดลองนี้ดูเหมือนจะเป็นว่าใน Forth มีอักขระหนึ่งตัวที่นิยามใหม่ไม่ได้ คือ ช่องว่าง และจะเกิดอะไรขึ้นถ้าลบข้อจำกัดนั้นออก
      ส่วนที่พูดถึงการ bootstrap นั้นจริง ๆ คือช่วงที่กำลังบอกผู้อ่านให้ถือว่าช่องว่างกับการขึ้นบรรทัดใหม่เป็นตัวคั่น
      พูดอีกอย่างคือกำลังบ่นว่าช่องว่างมีความหมายอยู่ในส่วนที่มันถูกประกาศให้เป็นตัวคั่น
      แน่นอนว่าจะคิดแบบนั้นก็ได้ แต่ก็อดสงสัยไม่ได้ว่ามีวิธีทำสิ่งนี้ให้ดีกว่านี้ไหม
    • อักขระช่องว่างเหล่านั้นคือวิธีที่ทำให้ช่องว่างซึ่งก่อนหน้านี้ไม่ได้ต่างจากอักขระอื่น กลายเป็น ช่องว่างจริง ๆ
      อย่างน้อยครั้งหนึ่งฉันก็นึกวิธีทำสิ่งนี้ไม่ออกเลย หากไม่ให้ช่องว่างตามตัวอักษรมีความหมายในลักษณะนั้น
  • เขาบอกว่าเป็น "กับดักของการมีไวยากรณ์บางรูปแบบ" แต่ ไวยากรณ์ ทำหน้าที่ให้โครงสร้าง
    คิดหรือว่าสามารถอ่านประโยคอย่าง "sentence this without you syntax read can" ได้โดยไม่มีไวยากรณ์?
    แม้ Cognition จะใช้ anti-syntax แบบ postfix สมบูรณ์ แต่ postfix ก็ยังเป็นไวยากรณ์
    ลองไปถามผู้พูดภาษาเยอรมันเรื่องกริยาที่ไปอยู่ท้ายประโยคดูก็ได้
    แม้แต่ในตัวอย่างแรก ลำดับของ operand และ operator ก็ยังสำคัญ และนั่นแหละคือไวยากรณ์
    สิ่งนี้ดูเหมือนความพยายามสร้างภาษาที่บีบอัดอย่างไร้เหตุผล และชวนให้นึกถึง APL มาก
    ขอฝากคำใบ้ถึงผู้เขียนว่า คุณไม่ได้ลบไวยากรณ์ออกไป แค่ทำให้มนุษย์อ่านและเข้าใจได้ยากขึ้นเท่านั้น และ ความสามารถในการอ่านและความเข้าใจได้ เป็นองค์ประกอบสำคัญในการเขียนโปรแกรม
  • อ่านแล้วค่อนข้างยากนิดหน่อย
    ให้ความรู้สึกเหมือนกฎใต้เท้าเปลี่ยนอยู่ตลอด ราวกับว่ากฎและคำต่าง ๆ ถูกแนะนำขึ้นมาแล้วก็นิยามใหม่ตามอำเภอใจ
    โดยรวมแล้วมีกลิ่นอายแบบ Numberwang อยู่ ซึ่งดูเป็นหนึ่งในเหตุผลที่ทำให้คนรับมันเป็นงานเสียดสี
    อีกเหตุผลใหญ่คือขั้นตอน bootstrap ถูกเขียนออกมาอย่างชวนขำเกินจริง ซึ่งก็ดูเหมือนตั้งใจไว้
    เห็นได้ชัดว่ามีอะไรลึกซึ้งอยู่ แต่คงต้องไปดื่มกาแฟที่เข้มกว่านี้แล้วค่อยกลับมาอ่านใหม่
    • มีหลายอย่างที่ต้องอธิบาย และฉันก็คิดว่าวิธีอธิบายตอนนี้อาจยังไม่ดีที่สุด
      ฉันเป็นผู้เขียนบทความนี้เอง และปัญหาคือมีสิ่งที่ต้องสื่อสารเยอะมากจริง ๆ
      ฉันกับ Matthew คุยกันวันละหลายชั่วโมงต่อเนื่องอยู่ 3 สัปดาห์เกี่ยวกับการออกแบบภาษานี้ และยังมีบริบทอีกมากที่ต้องเติมให้คนที่ไม่ได้รู้จักฉันเป็นการส่วนตัว