เจ็ดภาษาแม่แบบของการเขียนโปรแกรม (2022)
(madhadron.com)- ความแตกต่างของ ชุดรูปแบบพื้นฐาน สำคัญกว่าความต่างของไวยากรณ์รายภาษา และภาษาโปรแกรมแบ่งได้เป็น เจ็ดภาษาแม่แบบ ตามวิธีทำซ้ำ การเรียกซ้ำ และการประกอบโปรแกรม
- ALGOL, Lisp, ML, Self, Forth, APL, Prolog คือหมวดหลัก โดยแต่ละสายใช้ภาษาตัวแทนเป็นเหมือนตัวอย่างมาตรฐานเพื่อพิจารณาสายตระกูลของภาษาอื่น
- ภาษาใหม่ที่ใช้ภาษาแม่แบบซึ่งคุ้นเคยอยู่แล้วจะเรียนได้ง่าย แต่เมื่อย้ายไปยังภาษาแม่แบบที่ไม่คุ้น จะต้องใช้ แนวทางการคิดใหม่ และเวลาเรียนรู้อย่างมาก
- ALGOL เด่นที่การจัดองค์กรฟังก์ชันโดยมีการกำหนดค่า คำสั่งเงื่อนไข และลูปเป็นศูนย์กลาง, Lisp เด่นที่ แมโครและโค้ดแบบลิสต์, ML เด่นที่ฟังก์ชันชั้นหนึ่งและการเรียกซ้ำ, Self เด่นที่อ็อบเจ็กต์ส่งข้อความหากัน, Forth เด่นที่ไวยากรณ์แบบอิงสแตก, APL เด่นที่อาร์เรย์หลายมิติ, และ Prolog เด่นที่โครงสร้างข้อเท็จจริงกับการค้นหา
- สำหรับโปรแกรมเมอร์ทุกคน การชำนาญ ภาษาตระกูล ALGOL ควรเป็นลำดับแรก จากนั้นเรียน SQL แล้วค่อยฝึกภาษาแม่แบบที่ไม่คุ้นอย่างต่อเนื่อง ซึ่งให้ผลดีในระยะยาว
เจ็ดภาษาแม่แบบของการเขียนโปรแกรม
- เวลาจะเลือกภาษาโปรแกรม สิ่งสำคัญกว่าความต่างของไวยากรณ์รายภาษาคือการเรียนรู้ รูปแบบพื้นฐาน และในหมู่ภาษาสายใกล้กัน โครงสร้างพื้นฐานอย่างการวนดูอาร์เรย์หรือการวนดูชุดจัดเรียงมักมีรูปแบบแทบเหมือนกัน
- กลุ่มภาษาที่ต่างกันจะมีวิธีทำซ้ำ การเรียกซ้ำ และการจัดองค์ประกอบโปรแกรมต่างกันมาก และ ชุดรูปแบบพื้นฐาน เหล่านี้เองที่ก่อเป็นภาษาแม่แบบที่ต่างกัน
- การเรียนภาษาใหม่ที่ใช้ภาษาแม่แบบเดิมที่คุ้นเคยอยู่แล้วถือเป็นการเปลี่ยนผ่านที่ค่อนข้างง่าย แต่เมื่อย้ายไปยังภาษาแม่แบบที่ไม่คุ้น จะต้องใช้เวลามากและต้องมีแนวทางการคิดใหม่
- ภาษาแม่แบบที่วงการซอฟต์แวร์รับรู้มีอยู่เจ็ดแบบคือ ALGOL, Lisp, ML, Self, Forth, APL, Prolog
- ภาษาแม่แบบแต่ละแบบใช้ภาษาตัวแทนเฉพาะเป็นเหมือนตัวอย่างมาตรฐานในการจัดหมวด และภาษาที่เหลือจะถูกพิจารณาสายตระกูลโดยเทียบกับตัวอย่างนั้น
-
ALGOL
- โปรแกรมประกอบจากลำดับของ การกำหนดค่า คำสั่งเงื่อนไข และลูป และจัดระเบียบเป็นหน่วยฟังก์ชัน
- หลายภาษาเพิ่มระบบโมดูล วิธีนิยามชนิดข้อมูลใหม่ ภาวะพหุสัณฐาน และโครงสร้างควบคุมทางเลือกอย่างข้อยกเว้นหรือ coroutine เข้าไปจากฐานนี้
- ภาษาโปรแกรมส่วนใหญ่ที่ใช้อย่างแพร่หลายในปัจจุบันอยู่ในสายภาษาแม่แบบนี้
- ตัว ALGOL เองรวมถึง ALGOL 58, ALGOL 60, ALGOL W, ALGOL 68
- Assembly language, Fortran, C, C++, Python, Java, C#, Ruby, Pascal, JavaScript, Ada เชื่อมโยงกับสายนี้
- เป็นภาษาแม่แบบที่เก่าแก่ที่สุด โดยมีเชื้อสายย้อนกลับไปได้ถึงการทำให้โปรแกรมสำหรับเครื่องวิเคราะห์เชิงกลของ Babbage เป็นรูปแบบทางการโดย Ada Lovelace
- ภาษาเครื่องและภาษาแอสเซมบลีของคอมพิวเตอร์สถาปัตยกรรม Eckert-Mauchly ที่ต่อเนื่องมาถึง EDVAC และ Univac รุ่นแรก ตลอดจนความพยายามทำภาษาระดับสูงยุคต้นตั้งแต่ A-0 ของ Grace Hopper ไปจนถึง Fortran และ COBOL ล้วนอยู่ในรูปแบบนี้
- ในทศวรรษ 1960 วงวิชาการพัฒนาแนวคิดการเขียนโปรแกรมเชิงโครงสร้างเพื่อทำให้ภาษาเหล่านี้จัดการได้ง่ายขึ้น และผลลัพธ์คือ ALGOL 60 ซึ่งต่อมาสมาชิกส่วนใหญ่ของสายนี้ก็แตกแขนงจากจุดนั้น
- เมื่อเวลาผ่านไป มีแนวโน้มที่จะดูดซับคุณสมบัติจากภาษาแม่แบบอื่น
- ในทศวรรษ 1980 มีการนำ แนวคิดจากสาย Self มาผสานในรูปแบบคลาส เพื่อใช้เป็นวิธีนิยามชนิดข้อมูลและทำพหุสัณฐาน
- หลังปี 2010 ก็เริ่มมี แนวคิดจากสาย ML ปรากฏเข้ามา
-
Lisp
- เป็นไวยากรณ์ที่ผสาน นิพจน์ prefix ที่ล้อมด้วยวงเล็บ เข้ากับการแทนลิสต์
(+ 2 3)(defun square (x) (* x x))(* (square 3) 3)
- การแทนแบบ ลิสต์ ที่ล้อมรายการซึ่งคั่นด้วยช่องว่างไว้ในวงเล็บถูกฝังอยู่ในภาษา ทำให้โค้ดเองก็อยู่ในรูปของลิสต์
- แมโครสามารถรับลิสต์มาแก้ไข แล้วส่งโค้ดที่แก้แล้วต่อให้คอมไพเลอร์ได้ จึงเป็นโครงสร้างที่เปิดให้โปรแกรมเมอร์นิยามความหมายของภาษาใหม่ได้
- ในการเขียนโค้ดส่วนใหญ่ มักมีพฤติกรรมคล้ายภาษาแม่แบบอื่น โดยมากคือ ALGOL หรือ ML แต่ ระบบแมโคร คือจุดที่ทำให้ต่างออกไป
- ไวยากรณ์
loopของ Common Lisp ก็ไม่ได้เป็นความสามารถที่ฝังในภาษาโดยตรง แต่ถูกนิยามเป็นแมโคร - แม้ช่วงแรกจะมี Lisp หลากหลายสายย่อย แต่ชุมชนก็ค่อย ๆ สร้างฉันทามติที่ Common Lisp
- Sussman และ Steele สำรวจว่าฟังก์ชันทำอะไรได้ไกลแค่ไหน จนสร้าง Scheme ขึ้นมา
- มีการใช้ Lisp เพื่อวัตถุประสงค์เฉพาะ เช่น Lush สำหรับการคำนวณเชิงตัวเลข, AutoLISP เป็นภาษาสคริปต์ของ AutoCAD, และ Emacs Lisp สำหรับกำหนดพฤติกรรมการแก้ไขของ Emacs
- ระยะหลัง Clojure ก้าวขึ้นมาเป็นแขนงหลักลำดับที่สามของสาย Lisp
- เป็นตระกูลภาษาที่เก่าแก่เป็นอันดับสองที่ยังใช้อยู่จนถึงปัจจุบัน โดยเกิดตามหลัง Fortran ราวหนึ่งปี
- จุดเริ่มต้นของมันคือ คำถามทางคณิตศาสตร์ ว่าจะเขียนแทนโครงสร้างทางคณิตศาสตร์ที่ประเมินนิพจน์ของตัวเองได้อย่างไร
- John McCarthy ให้คำตอบไว้ในปี 1958 และต่อมาจึงถูกนำไปทำงานบนคอมพิวเตอร์
- Lisp ยุคแรก ๆ เข้ากันได้ไม่ดีกับเครื่องในยุคนั้นเพราะพื้นฐานทางคณิตศาสตร์ของมัน ปัญหาเรื่องหน่วยความจำและรอบ CPU เป็นเรื่องที่ไม่มีในคณิตศาสตร์ และทำให้ต้องมีเทคนิคอย่าง garbage collection
- ช่วงปลายทศวรรษ 1970 ถึงต้นทศวรรษ 1980 มีเครื่องที่ออกแบบขึ้นใหม่ทั้งหมดเพื่อรัน Lisp โดยเฉพาะ
- องค์ประกอบหลายอย่างของ IDE ในปัจจุบันก็ถูกคิดค้นขึ้นบนเครื่องเหล่านั้น
- ในช่วงเวลาเดียวกัน Lisp เป็นเครื่องมือหลักของงานวิจัยปัญญาประดิษฐ์ และเมื่อกระแส AI ร้อนแรงในทศวรรษ 1980 ไม่ก่อผลลัพธ์ตามคาด Lisp ก็ร่วงลงสู่ AI Winter ไปพร้อมกับทั้งวงการ
- หลังจากนั้นมันก็ยังอยู่รอดมาได้ และด้วยสมรรถนะคอมพิวเตอร์ที่ดีขึ้นรวมถึงภาษารุ่นอื่นรับคุณสมบัติของมันไปใช้ ความยากในการทำให้มันใช้งานได้จริงจึงลดลง
- เป็นไวยากรณ์ที่ผสาน นิพจน์ prefix ที่ล้อมด้วยวงเล็บ เข้ากับการแทนลิสต์
-
ML
- ฟังก์ชันเป็น ค่าชั้นหนึ่ง และมี ระบบชนิดแบบ Hindley-Milner ที่สามารถแสดงฟังก์ชันได้หลากหลายและ tagged union ได้
- การทำซ้ำทั้งหมดเกิดขึ้นในรูปของการเรียกซ้ำ
sum [] = 0sum (x:xs) = x + sum xs
- ยังใช้วิธีนิยามฟังก์ชันที่ห่อหุ้มรูปแบบการทำซ้ำไว้ แล้วรับฟังก์ชันอื่นมาเพื่อกำหนดพฤติกรรม
map _ [] = []map f (x:xs) = (f x) : (map f xs)
- บางภาษาอย่าง Miranda และ Haskell ใช้การประเมินค่าแบบขี้เกียจเป็นค่าเริ่มต้น
- ภาษาอื่น ๆ ขยายระบบชนิดไปในหลายทิศทาง
- OCaml พยายามผสานกับแนวคิดของภาษาแม่แบบ Self
- Agda และ Idris เลือกระบบชนิดเชิงพึ่งพา ที่ผสมค่าและชนิดเข้าด้วยกัน
- 1ML ผสานโมดูลกับชนิด
- จาก ML แตกแขนงเป็น CaML, Standard ML, OCaml
- และยังมีสายที่เกี่ยวข้องอย่าง Miranda, Haskell, Agda, Idris ต่อเนื่องออกไป
- ML เดิมเป็น metalanguage ของโปรแกรมพิสูจน์ทฤษฎีบทที่พัฒนาขึ้นใน Cambridge ประเทศอังกฤษ และชื่อของมันก็มาจากตรงนี้
- ต่อมามันหลุดออกจากบริบทเดิมและแพร่หลายเป็นภาษาอิสระ โดยได้รับความนิยมในยุโรป โดยเฉพาะสหราชอาณาจักรและฝรั่งเศส
-
Self
- โปรแกรมประกอบจาก ชุดของอ็อบเจ็กต์ ที่ส่งข้อความหากัน และทุกการทำงานถูกทำผ่านวิธีนี้
- อ็อบเจ็กต์ใหม่ถูกสร้างขึ้นโดยการส่งข้อความไปยังอ็อบเจ็กต์เดิม
- แม้แต่คำสั่งเงื่อนไขก็ทำงานผ่านตัวแปรที่อ้างถึงอ็อบเจ็กต์ true หรือ false ตัวใดตัวหนึ่ง
- อ็อบเจ็กต์ทั้งสองรับข้อความที่มีฟังก์ชันสำหรับกรณีจริงและกรณีเท็จเป็นพารามิเตอร์
- อ็อบเจ็กต์ true จะรันฟังก์ชันตัวแรก ส่วนอ็อบเจ็กต์ false จะรันฟังก์ชันตัวที่สอง
- โค้ดฝั่งเรียกไม่รู้ว่าเป็นอ็อบเจ็กต์ใด รู้เพียงแค่ว่าส่งข้อความออกไป
- ลูปก็ทำแบบเดียวกัน และหากสร้างอ็อบเจ็กต์ที่เหมาะสมแล้ววางไว้ในตำแหน่งที่เหมาะสม ก็สามารถ นิยามความหมายของภาษาใหม่ทั้งหมด ได้
- ภาษาเหล่านี้มักเก็บซอร์สไว้ใน สภาพแวดล้อมแบบ live ไม่ใช่ไฟล์ข้อความ
- โปรแกรมเมอร์จะแก้ไขระบบที่กำลังทำงานอยู่ แล้วบันทึกสถานะของมัน แทนที่จะคอมไพล์ไฟล์เพื่อสร้างระบบ
- ตัวอย่างสำคัญคือ Smalltalk และ Self
- หลายภาษานำวิธีส่งข้อความของตระกูลนี้ไปใช้เพียงบางส่วน และการนำไปใช้แบบบางส่วนนี้มักถูกเรียกว่า object-oriented programming
- ส่วนใหญ่มีพื้นฐานจาก Smalltalk โดยมี JavaScript เป็นข้อยกเว้นที่สืบมาจากระบบอ็อบเจ็กต์ไร้คลาสของ Self
- ระบบอ็อบเจ็กต์ของ Common Lisp ทำให้กว้างขึ้นจากอ็อบเจ็กต์ผู้รับข้อความเพียงตัวเดียว ไปเป็นการให้รันไทม์เลือกโค้ดที่จะรันตาม พารามิเตอร์ทุกตัว
- Erlang เปลี่ยนแนวทางจากการให้สายการทำงานย้ายไปมาระหว่างอ็อบเจ็กต์ มาเป็นการให้ เธรดที่ทำงานขนานกัน ฟังและส่งข้อความอย่างชัดเจน
- ภาษาต้นกำเนิดคือ Smalltalk ซึ่งพัฒนาที่ Xerox Parc ในช่วงปลายทศวรรษ 1970 และ 1980
- ในทศวรรษ 1980 มีระบบ Smalltalk เชิงพาณิชย์หลายตัว และ IBM ก็ใช้ Smalltalk ในการพัฒนา VisualAge ซึ่งเป็นชุดเครื่องมือเขียนโปรแกรมสำหรับภาษาอื่น
- ปัจจุบัน Smalltalk ยังดำรงอยู่เป็นหลักในรูปของโอเพนซอร์ส Pharo Smalltalk
- มีงานวิจัยจำนวนมากเกี่ยวกับการรัน Smalltalk ให้เร็วและมีประสิทธิภาพ โดยจุดสูงสุดคือ โครงการ Strongtalk
- ความค้นพบจาก Strongtalk มีความสำคัญทางประวัติศาสตร์ เพราะกลายเป็นฐานของ คอมไพเลอร์ JIT HotSpot ของ Java
- Smalltalk รับแนวคิดเรื่องค่าและชนิดจากภาษารุ่นก่อนมาสร้างคลาส โดยทุกอ็อบเจ็กต์มีคลาสที่กำหนดชนิดของมัน และคลาสนั้นก็สร้างอ็อบเจ็กต์ของชนิดนั้นขึ้นมา
- Self ตัดแนวคิดเรื่องคลาสออกไป และประกอบด้วยอ็อบเจ็กต์ล้วน ๆ
- ด้วยเหตุที่เป็นรูปแบบที่บริสุทธิ์กว่า จึงเลือก Self เป็นตัวอย่างมาตรฐานของภาษาแม่แบบนี้
-
Forth
- ภาษาสแตกมีลักษณะเหมือน ภาพกลับของ Lisp และใช้ไวยากรณ์ร่วมกับเครื่องคิดเลขแบบ reverse Polish notation ของ Hewlett Packard
- มันมี data stack และเมื่อเขียนลิเทอรัลอย่าง
42ค่านั้นจะถูก push ลงสแตก ส่วนชื่อฟังก์ชันจะทำงานกับสแตกโดยไม่มีพารามิเตอร์ที่ระบุไว้ชัดเจน - แม้แต่เลขคณิตง่าย ๆ ก็อยู่ในรูปกลับด้าน เช่น
2 3 + 5 * - การนิยามฟังก์ชันก็สั้นกระชับมาก
- ใน Forth ส่วนใหญ่
:ใช้สำหรับนิยามคำใหม่ squareมีความหมายเท่ากับการเรียกdupและ*dupคัดลอกค่าบนสุดของสแตก ส่วน*คูณสองรายการบนสุด
- ใน Forth ส่วนใหญ่
- มันสามารถดัก parser แล้วแทนที่ด้วยโค้ดของตัวเองได้ จึง เปลี่ยนไวยากรณ์ทั้งชุดได้
- จึงพบโปรแกรม Forth ที่นิยามภาษาย่อยขึ้นมาเป็นเรื่องปกติ เช่น การพาร์สส่วนย่อยของ Fortran, packet layout, หรือไดอะแกรม ASCII ที่แทนการเปลี่ยนสถานะของ state machine โดยตรง
- มีทั้ง Forth หลายสายย่อย รวมถึง PostScript, Factor, Joy
- Joy เป็นภาษาฟังก์ชันบริสุทธิ์ที่ใช้รูปแบบทางคณิตศาสตร์ของการประกอบแทนการใช้สแตก
- Forth ถูกเขียนขึ้นครั้งแรกในปี 1970 เพื่อ ควบคุมกล้องโทรทรรศน์วิทยุ
- หลังจากนั้นก็แพร่หลายไปทั่วโลกของระบบฝังตัว
- ระบบ Forth ทำ bootstrap ได้ง่ายมากพอที่โปรแกรมเมอร์จำนวนมากจะสร้างสายย่อยของตัวเองตามวัตถุประสงค์เฉพาะ จนมีอยู่หลายสิบแบบ
- PostScript ปรากฏขึ้นในทศวรรษ 1980 ในฐานะวิธีที่ยืดหยุ่นสำหรับอธิบายเอกสารแก่เครื่องพิมพ์
- PostScript แม้จะถูกจำกัดมากกว่า Forth ในหลายด้าน แต่ก็นิยามปฏิบัติการพื้นฐานด้านการจัดวางกราฟิกไว้ในภาษา
-
APL
- ทุกอย่างในภาษาคือ อาร์เรย์หลายมิติ
- ตัวดำเนินการประกอบจากสัญลักษณ์หนึ่งหรือสองตัว และทำปฏิบัติการระดับสูงกับอาร์เรย์ทั้งชุด
- การแสดงผลมีความกระชับมาก จนชุดสัญลักษณ์เองกลายเป็นร่องรอยของการคำนวณโดยแทบไม่ต้องตั้งชื่ออย่างอื่นเพิ่ม
- ตัวอย่างเช่น การคำนวณค่าเฉลี่ยของตัวแปร
xเขียนได้เป็น(+⌿÷≢) x - APL, J, K คือกรณีตัวแทนสำคัญ
- ปฏิบัติการระดับสูงกับอาร์เรย์บางส่วนถูกส่งต่อออกไปยังสภาพแวดล้อมมากมาย เช่น MATLAB, NumPy, R
- APL เริ่มต้นจากสัญกรณ์ทางคณิตศาสตร์ที่ Kenneth Iverson สร้างขึ้นในทศวรรษ 1960 แล้วจึงถูกนำไปทำงานบนคอมพิวเตอร์
- หลังจากนั้นมันก็มีฐานผู้สนับสนุนเฉพาะกลุ่มอย่างต่อเนื่องในหมู่ผู้ทำงานคำนวณหนัก
- ภาษาลูกหลานอย่าง K ได้รับความนิยมอย่างมากในวงการการเงิน
-
Prolog
- โปรแกรมประกอบจากชุดของข้อเท็จจริง
father(bob, ed).father(bob, jane).
- ยังใช้ ข้อเท็จจริงที่ไม่ถูกกำหนดค่าแน่นอน เพื่ออนุมานข้อเท็จจริงจากข้ออื่น
grandfather(X, Y) :- father(X, Z), father(Z, Y).
- รันไทม์ของ Prolog จะรับข้อเท็จจริงเหล่านี้พร้อมกับคำถาม แล้วทำการค้นหาเพื่อหาผลลัพธ์
- หากเลือกรูปแบบนิยามข้อเท็จจริงได้เหมาะสม ก็จะได้ Turing completeness
- เทอมที่ประกอบเป็นข้อเท็จจริงใน Prolog นั้นเป็นชนิดข้อมูลเฉพาะของมันเอง และสามารถสร้างขึ้นแล้วส่งต่อให้รันไทม์ได้
- จุดนี้มีตำแหน่งคล้ายกับแมโครของ Lisp หรือการสลับ parser ของ Forth
- เนื่องจากโปรแกรม Prolog โดยเนื้อแท้คือการค้นหา การปรับแต่งจึงมักเน้นที่ การกำหนดลำดับการค้นหา และการตัดเส้นทางที่ไม่ก่อผลลัพธ์ตั้งแต่เนิ่น ๆ คล้ายกับงาน query ฐานข้อมูล
- รวมถึง Prolog, Mercury, Kanren
- การเขียนโปรแกรมจริงในตระกูลภาษาแม่แบบนี้ส่วนใหญ่เกิดขึ้นใน Prolog เอง และชุมชนก็มีเอกภาพสูงมาก
- ในทศวรรษ 1970 นักตรรกะชาวฝรั่งเศสตระหนักว่าสามารถแสดงโปรแกรมเป็น ตรรกะอันดับหนึ่ง ได้ และเริ่มพยายามนำไปทำจริง
- ในทศวรรษ 1980 โครงการคอมพิวเตอร์ยุคที่ห้าของญี่ปุ่นเดิมพันกับ Prolog อย่างมาก แต่เมื่อโครงการล้มเหลว ชื่อเสียงของ Prolog ก็ลดลงตามไปด้วย
- แม้อย่างนั้น งานวิจัยเพื่อทำให้รันไทม์ของ Prolog มีประสิทธิภาพในกรณีส่วนใหญ่ พร้อมทั้งเพิ่มความสามารถใหม่ ๆ ก็ยังดำเนินต่อเนื่องมาหลายทศวรรษ
- เมื่อมีการเพิ่มความสามารถอย่าง ข้อจำกัดเชิงตัวเลข ก็พัฒนาไปสู่ constraint logic programming
- Prolog ยังคงปรากฏอยู่ในพื้นที่เฉพาะทางต่อเนื่อง
- การตรวจชนิดของ Java เคยถูกเขียนด้วย Prolog อยู่หลายปี
- เครื่องมือค้นหาซอร์สโค้ดยุคแรกของ Facebook ก็มีพื้นฐานจาก Prolog
- โปรแกรมประกอบจากชุดของข้อเท็จจริง
จะนำไปใช้อย่างไร
- สำหรับโปรแกรมเมอร์ส่วนใหญ่ ภาษาเหล่านี้บางส่วนหรือทั้งหมดอาจดูแปลกมาก แต่เพื่อ แนวทางการคิด และความเป็นไปได้ใหม่ ๆ ที่แต่ละแบบเปิดให้ การลงทุนเวลาเรียนรู้ย่อมคุ้มค่า
- จากมุมมองของ ALGOL สิ่งสองอย่างอาจดูต่างกันอย่างสิ้นเชิง แต่จากมุมมองอื่นกลับเป็นเพียงความต่างเล็กน้อย ซึ่งเกิดขึ้นบ่อยมาก
-
ลำดับความสำคัญ
- โปรแกรมเมอร์ทุกคนควรรู้จัก ภาษาตระกูล ALGOL อย่างน้อยหนึ่งภาษาให้ดี
- หลังจากนั้นแนะนำให้เรียน SQL ซึ่งเป็นภาษาตระกูล Prolog
- มันอยู่ในตำแหน่งที่ให้ประโยชน์ต่ออาชีพมากที่สุดรองจาก ALGOL
-
การขยายต่อจากนั้น
- เมื่อเชี่ยวชาญสองตระกูลข้างต้นแล้ว ในระยะยาวจะคุ้มค่าหากเรียนภาษาใหม่จากตระกูลภาษาแม่แบบที่ไม่คุ้นปีละหนึ่งภาษา
- ภาษาและลำดับที่เสนอในแต่ละตระกูลมีดังนี้
- Lisp: PLT Racket
- ML: Haskell
- Self: Self
- Prolog: Prolog
- Forth: gForth
- APL: K ใช้งานผ่าน
ok
-
การปรับลำดับ
- หากทำงานคำนวณเชิงตัวเลขมาก ควรเรียน K ให้เร็วขึ้น
- หากทำงานเขียนโปรแกรมระบบฝังตัวมาก ควรเรียน gForth ให้เร็วขึ้น
- แต่ลำดับเองหรือการเลือกว่าจะเอาภาษาใดเป๊ะ ๆ ไม่ใช่เรื่องสำคัญมาก
- จะเรียน Standard ML หรือ OCaml แทน Haskell, Common Lisp แทน PLT Racket, หรือ Factor แทน gForth ก็ได้เช่นกัน
-
ข้อมูลเสริมในเชิงอรรถ
- แม้จะเรียน SQL แล้ว ก็ยังควรเรียน Prolog เอง อยู่ดี
- เพราะวิธีใช้งานจริงต่างจาก SQL พอสมควร
- มีความเห็นจากผู้อ่านว่าหากจะเข้าใจ Forth อย่างลึกซึ้ง วิธีที่พบได้บ่อยคือการลองสร้างตัวแปลภาษา Forth ด้วยตัวเอง
- มีการกล่าวว่า Forth เล็กพอที่คนคนเดียวจะสร้างจากศูนย์ได้ในเวลาไม่นานนัก
- gForth เป็นตัวแปลภาษาที่เหมาะสำหรับเรียน ANS Forth
- มีการกล่าวถึง FORTH Fundamentals, Volume 1 ของ McCabe เป็นสื่อการเรียนรู้
- และมีการกล่าวถึง PygmyForth, eForth, colorForth เป็น Forth ที่ควรดูประกอบ
- แม้จะเรียน SQL แล้ว ก็ยังควรเรียน Prolog เอง อยู่ดี
5 ความคิดเห็น
น่าสนุกดีนะ
ตอนเรียนมหาวิทยาลัย ผมเคยเรียนวิชาเอกและทำการบ้านด้วยภาษาในสาย ALGOL, Lisp และ Prolog พอมาเห็นแล้วก็ทำให้นึกถึงความหลังขึ้นมาเลยครับ
ภาษาเหล่านั้นทิ้งอิทธิพลไว้กับภาษาโปรแกรมกระแสหลักสมัยใหม่มากทีเดียว
แต่ในบรรดานั้น ดูเหมือนมีแค่ Forth ที่อิทธิพลจะน้อยหน่อยนะครับ
ถึงจะพอไม่ต้องถึงขั้นสัญกรณ์นำหน้า แต่ถ้าจะเขียนโค้ดด้วยสัญกรณ์หลังนี่ไม่สะดวกเกินไปจริง ๆ
ความคิดเห็นจาก Hacker News
ตอนเรียนวิชา PL ที่ Tufts เคยได้ลองสร้างภาษาตระกูล imperative, Lisp, ML, Smalltalk แบบมินิด้วยตัวเองอย่างละตัว และก็ดีใจที่ตอนนี้กระบวนการนั้นมีออกมาเป็นตำราเรียนแล้ว เมื่อก่อนยังมีส่วนของ Prolog ด้วย เลยเสียดายที่ตอนนี้หายไป
ถ้าจะขอแก้การจัดหมวดหมู่ในบทความนี้สักอย่าง ผมมองว่า Ruby ไม่ได้เป็นสาย Algol เท่าไร แต่ชัดเจนกว่าในฐานะภาษาเชิงวัตถุ ได้อิทธิพลจาก Smalltalk มาก และแม้แต่ชื่อใน standard library ก็ยังมีร่องรอยนั้นอยู่ เช่นใช้
collectมากกว่าmapอะไรทำนองนั้น ใน Ruby ทุกอย่างเป็นอ็อบเจ็กต์ตั้งแต่ต้นจนจบ และการเรียกเมธอดก็เข้าใจได้อย่างเป็นธรรมชาติกว่าในฐานะการส่งข้อความไปยังอ็อบเจ็กต์ มักถูกเอาไปเทียบกับ Python แต่เส้นทางวิวัฒนาการค่อนข้างต่างกัน และตอนนี้เหมือนจะมาบรรจบกันที่จุดคล้ายกันในเชิง ecosystem สำหรับผม Ruby ให้ความรู้สึกเหมือน อัลปาก้าแสนนุ่มฟู มากกว่า PythonHello Worldแต่แม้แต่ชนิดข้อมูลพื้นฐานก็กลายเป็นอ็อบเจ็กต์ทั้งหมด ถ้าจะอธิบายให้คนที่ไม่ชอบ OOP เห็นภาพ ก็เอาtype(42)กับdir(42)ให้ดูเพื่อเน้นว่าแม้แต่จำนวนเต็มก็เป็นอ็อบเจ็กต์ผมอยากเพิ่มหมวด ภาษาสำหรับแสดงการพิสูจน์ เข้าไปในลำดับวงศ์ภาษาด้วย เป็นสายที่โปรแกรมก็คือบทพิสูจน์ตาม Curry-Howard correspondence โดย Lean เป็นตัวอย่างเด่น จะมองว่าเป็นหมวดย่อยของ functional ก็ได้ แต่เพราะจุดประสงค์หลักคือการตรวจพิสูจน์มากกว่าการรัน เลยรู้สึกว่าควรมีแกนแยกต่างหาก
ช่วงหลังผมกลับไปดูโปรเจ็กต์เปรียบเทียบภาษาอีกครั้ง เป็นเบนช์มาร์กที่ทำ cycle decomposition แบบขนานของ signed permutation จำนวน 3,715,891,200 แบบสำหรับตัวอักษร 10 ตัว สิ่งที่อยากรู้ไม่ใช่ “ภาษาต้นแบบ” แต่คือในบรรดา ภาษายุคปัจจุบัน ของแต่ละพาราไดม์นั้น มีภาษาไหนที่น่าจะเลือกใช้จริงสำหรับงานเขียนโปรแกรมเชิงวิจัย นอกจากประสิทธิภาพแล้วก็ยังดูด้วยว่าขอความช่วยเหลือจาก AI ได้ง่ายไหม และผมเองอ่านโค้ดกับคิดตามได้สบายหรือเปล่า อีกทั้งเพราะมี AI ช่วย เลยได้ลอง optimize แต่ละภาษาอย่างลึกพอสมควรเหมือนไปเที่ยวชม ผลสรุปอยู่ที่นี่ และโดยเฉพาะที่ F# ขึ้นมาอยู่อันดับบนสุดนี่ค่อนข้างน่าประหลาดใจ
ผมเองก็เคยเขียนบทความคล้าย ๆ กันไว้ที่นี่ ผมเห็นด้วยกับ Algol, Lisp, Forth, APL, Prolog แต่สำหรับภาษาฟังก์ชันที่พลิกวงการ ผมใส่ SASL ซึ่งมาก่อน ML เล็กน้อย และในฐานะตัวแทนเชิงวัตถุ ผมเลือก Smalltalk ที่มาก่อน Self นอกจากนี้ยังใส่ Fortran, COBOL, SNOBOL และ Prograph ด้วย เพราะมองว่าแต่ละตัวเปลี่ยนเกมในคนละแบบ
ผมอยากเพิ่ม ตระกูลเชิงความหมาย เข้าไปในการสนทนานี้ด้วย เช่น Verilog, Petri nets, Kahn process networks, dataflow machines, process calculi, reactive, term rewriting, ตระกูล constraint solver/theorem prover, probabilistic programming และยังมีภาษาอย่าง Unison, Darklang, temporal dataflow, DBSP ที่แม้จะไม่เข้ากับ 7 หมวดเดิมพอดี แต่ในทางปฏิบัติก็ใกล้ระดับพร้อมใช้งานจริงแล้ว อาจดูเหมือนเล่นนอกกติกาไปหน่อย แต่ส่วนใหญ่เป็นโมเดลการคำนวณที่ขนานไปกับเครื่องแบบ von Neumann ผมอยากเขียนบทความแนว “ทุกวิธีการคำนวณที่เรารู้จัก นอกเหนือจาก von Neumann” มาสักพักแล้ว
1+1ให้เป็นADD(1,1)ก็จะ parse ได้ด้วยวิธีที่ผมคุ้นเคย แถมตอนนั้นยังดื้อไม่ยอมเรียน regex เลยทำให้โค้ดออกมาพิลึกพอสมควร และยังจำได้ว่ามีเพื่อนร่วมทีมพูดว่า “Andy บอกว่ามันใช้ได้ งั้นอย่าไปแตะมันเลย” ขณะที่อีกทีมหนึ่งใช้ regex ทำงานเดียวกันได้สั้นกว่าโค้ดผมราว 20 เท่าวิชา “Concepts of programming languages” ที่เคยเรียนที่ TU Delft เป็นวิชาที่ผมชอบที่สุดในสาขาคอมพิวเตอร์ เราเรียน C, ฝั่งฟังก์ชันผ่าน Scala, และ JavaScript ในฐานะตัวอย่างของแนวคิด prototype ซึ่งช่วยมากจนอีกหลายปีต่อมาตอนผมไปเรียน Elixir ก็ง่ายขึ้นเยอะ นอกจากนี้ยังมีวิชาที่ให้เขียนเอเจนต์ Unreal Tournament ด้วยภาษา GOAL ซึ่งเป็นภาษาแบบ Prolog-based ด้วย ผมเองอยู่นานมากกว่าจะเห็นภาพว่า Prolog เอาไปใช้ตรงไหนได้บ้าง แต่สุดท้ายก็ได้เอามันไปทำ spellcheck ที่บังคับให้ประโยค Papiamentu แย่ ๆ ที่ LLM สร้างขึ้นถูกแก้ซ้ำไปเรื่อย ๆ
ผมเห็นด้วยกับแนวคิดที่ว่า “ควรเรียนภาษาคนละประเภทกัน” พอได้เรียน OCaml ผมถึงเริ่มรู้สึกว่าฟังก์ชันคือฟังก์ชันทางคณิตศาสตร์จริง ๆ ส่วน Mathematica ก็ฝึกนิสัยให้มอง expression เองเป็นอินพุตได้ ส่วนสัญกรณ์ Polish แบบย้อนกลับของ PostScript ทำให้ผมรู้สึกเหมือนวิธีคิดถูกเดินสายใหม่ไปเลย ไม่ได้กระทบแค่คณิตศาสตร์พื้นฐานเท่านั้น แต่ผมไม่เห็นด้วยกับความคิดที่ว่าเลือก Java, C#, C++, Python หรือ Ruby ตัวไหนก็เหมือนกัน ถ้าเป้าหมายมีแค่เขียน quicksort มันอาจคล้ายกัน แต่สำหรับคนที่อยากสร้างของจริง การเลือกภาษาทำให้เกิดความต่างแบบ คนละโลก ถ้าคุณยื่น Ruby ให้คนที่อยากทำเกม 3D หรือยื่น Java ให้คนที่อยากทำ exploratory data science หรือ deep learning ก่อนเป็นภาษาแรก ก็อาจทำให้เขาหมดไฟได้
อ่านบทความนี้แล้วนึกถึง 7 languages in 7 weeks ของ Bruce Tate ผมเองก็รู้จัก Erlang ครั้งแรกจากหนังสือเล่มนั้น แต่ในเชิงประวัติศาสตร์ ผมรู้สึกว่าการจัด COBOL กับ Fortran ให้อยู่ในสาย Algol ค่อนข้างฝืน ถึงอย่างนั้นมันก็เตือนให้เห็นว่าประวัติศาสตร์เองโดยธรรมชาติก็ต้องมีการสรุปลดทอนอยู่บ้าง
มีประเด็นเกี่ยวข้องจากการถก HN ครั้งก่อนด้วย อ่านการสนทนาก่อนหน้า ประกอบจะช่วยเห็นบริบทมากขึ้น