การเขียนโปรแกรมด้วยภาษา Elixir
คู่มือที่ครอบคลุมเกี่ยวกับการเขียนโปรแกรมเชิงฟังก์ชันและแบบขนานด้วยภาษา Elixir โดยครอบคลุมการเปลี่ยนจากแนวคิดเชิงวัตถุไปสู่การคิดในเชิงฟังก์ชัน การจับรูปแบบ การไม่เปลี่ยนแปลงข้อมูล โมเดลผู้กระทำสำหรับการประมวลผลพร้อมกัน และการสร้างระบบกระจายที่มีความทนทานด้วย OTP
ภาพรวมคอร์สเรียน
📚 สรุปเนื้อหา
คู่มือที่ครอบคลุมเกี่ยวกับการเขียนโปรแกรมแบบฟังก์ชันและพร้อมใช้งาน (concurrent programming) โดยใช้ภาษา Elixir ครอบคลุมการเปลี่ยนจากแนวคิดเชิงวัตถุ (object-oriented) มาเป็นแนวคิดเชิงฟังก์ชัน การจับคู่รูปแบบ (pattern matching) ความไม่สามารถเปลี่ยนแปลงได้ (immutability) โมเดลผู้กระทำ (actor model) สำหรับการประมวลผลขนาน และการสร้างระบบกระจายที่มั่นคงด้วย OTP
เชี่ยวชาญศิลปะในการสร้างระบบที่ทนทานและทำงานพร้อมกันผ่านความงามของโปรแกรมเชิงฟังก์ชัน
ผู้เขียน: เดวิด โทมัส
คำขอบคุณ: โจเซ่ วาลิม, คอรี แฮนส์, บรูซ เทต, เจนนิเฟอร์ เคอร์ร์, แอนโธนี อีเดน, เชด ฟาวเลอร์, คิม ชไรม์, แคแนซ คันนิงแฮม และ พอโตมาค อินเด็กซ์
🎯 เป้าหมายการเรียนรู้
- เปรียบเทียบแนวทางการเปลี่ยนแปลงข้อมูลของภาษา Elixir กับการเขียนโปรแกรมแบบพึ่งพาสถานะ (state-based) ทั่วไป
- ตั้งค่าชัลล์อินเตอร์แอคทีฟของภาษา Elixir (IEx) และรันโค้ด Elixir ผ่านสคริปต์และคอมไพเลอร์
- ใช้ตัวดำเนินการจับคู่ (
=), ตัวดำเนินการแนบ (pin operator^), และตัวแทนที่ไม่สนใจ (_) เพื่อแยกโครงสร้างข้อมูลและตรวจสอบข้อมูล - อธิบายผลกระทบทางทฤษฎีและปฏิบัติของการไม่สามารถเปลี่ยนแปลงข้อมูลต่อประสิทธิภาพและจัดการหน่วยความจำ
- ระบุและใช้ประเภทข้อมูลที่มีอยู่ในตัวของภาษา Elixir รวมถึงประเภทค่า (Value), ระบบ (System), และประเภทคอลเลกชัน (Collection)
- ใช้กฎการจำกัดขอบเขตตัวแปร (variable scoping) และการใช้คำสั่ง
withเพื่อจัดการการเปลี่ยนแปลงข้อมูลที่ซับซ้อน - สร้างและเรียกใช้ฟังก์ชันที่ไม่มีชื่อ (anonymous functions) ทั้งแบบปกติและแบบแคปเจอร์ (
&) - ใช้การจับคู่รูปแบบ (pattern matching) และการเรียกซ้ำ (recursion) ภายในโมดูลเพื่อจัดการตรรกะที่ซับซ้อนโดยใช้ฟังก์ชันที่มีชื่อ
- ใช้เงื่อนไขควบคุม (guard clauses) และพารามิเตอร์เริ่มต้น (default parameters) เพื่อควบคุมลำดับการทำงานของฟังก์ชัน
- แยกโครงสร้างและสร้างรายการ: ใช้การจับคู่หัว/ก้อย (head/tail pattern matching) เพื่อสำรวจและสร้างโครงสร้างรายการแบบเรียกซ้ำ
🔹 บทที่ 1: บทนำสู่ภาษา Elixir และการจับคู่รูปแบบ
ภาพรวม: บทนี้แนะนำภาษา Elixir ว่าเป็นภาษาแบบฟังก์ชันที่เน้นการเปลี่ยนแปลงข้อมูล แทนที่จะเน้นการเปลี่ยนแปลงสถานะ นักเรียนจะได้เรียนรู้การเคลื่อนไหวในสภาพแวดล้อมของภาษา Elixir (IEx) การคอมไพล์และรันสคริปต์ และการเชี่ยวชาญการจับคู่รูปแบบ (Pattern Matching) — กลไกหลักที่ภาษา Elixir ใช้สำหรับการผูกตัวแปรและการควบคุมการไหลของโปรแกรม
ผลการเรียนรู้:
- เปรียบเทียบแนวทางการเปลี่ยนแปลงข้อมูลของภาษา Elixir กับการเขียนโปรแกรมแบบพึ่งพาสถานะทั่วไป
- ตั้งค่าชัลล์อินเตอร์แอคทีฟของภาษา Elixir (IEx) และรันโค้ด Elixir ผ่านสคริปต์และคอมไพเลอร์
- ใช้ตัวดำเนินการจับคู่ (
=), ตัวดำเนินการแนบ (pin operator^), และตัวแทนที่ไม่สนใจ (_) เพื่อแยกโครงสร้างข้อมูลและตรวจสอบข้อมูล
🔹 บทที่ 2: ความไม่สามารถเปลี่ยนแปลงและพื้นฐานของภาษา Elixir
ภาพรวม: บทนี้สำรวจปรัชญาพื้นฐานของภาษา Elixir: ความไม่สามารถเปลี่ยนแปลงได้ (immutability) นักเรียนจะได้เรียนรู้ว่าภาษา Elixir จัดการข้อมูลเป็นสิ่งที่ไม่สามารถเปลี่ยนแปลงได้ ประโยชน์ด้านประสิทธิภาพของแนวทางนี้ และประเภทข้อมูลที่มีอยู่ในตัว ตั้งแต่ประเภทค่าพื้นฐาน เช่น อะตอม (atoms) และช่วง (ranges) ไปจนถึงคอลเลกชันที่ซับซ้อน เช่น แมป (maps) และไบนารี (binaries) สุดท้ายบทนี้จะลงลึกไปที่การจำกัดขอบเขตตัวแปร และคำสั่ง with ที่ทรงพลัง
ผลการเรียนรู้:
- อธิบายผลกระทบทางทฤษฎีและปฏิบัติของการไม่สามารถเปลี่ยนแปลงข้อมูลต่อประสิทธิภาพและจัดการหน่วยความจำ
- ระบุและใช้ประเภทข้อมูลที่มีอยู่ในตัวของภาษา Elixir รวมถึงประเภทค่า (Value), ระบบ (System), และประเภทคอลเลกชัน (Collection)
- ใช้กฎการจำกัดขอบเขตตัวแปร (variable scoping) และคำสั่ง
withเพื่อจัดการการเปลี่ยนแปลงข้อมูลที่ซับซ้อน
🔹 บทที่ 3: ฟังก์ชัน โมดูล และเครื่องหมายท่อน (Pipe Operator)
ภาพรวม: บทนี้สำรวจแก่นกลางของการเขียนโปรแกรมในภาษา Elixir: การเปลี่ยนแปลงเชิงฟังก์ชัน ครอบคลุมการเปลี่ยนจากฟังก์ชันที่ไม่มีชื่อและคลอเจอร์ (closures) ไปสู่โมดูลที่จัดระเบียบและฟังก์ชันที่มีชื่อ นักเรียนจะได้เรียนรู้การใช้การจับคู่รูปแบบ การเรียกซ้ำ และเครื่องหมายท่อน (pipe operator) เพื่อสร้างโค้ดที่กระชับ อ่านง่าย และบำรุงรักษาง่าย พร้อมทั้งโต้ตอบกับเครื่องเสมือนเออร์แลง (Erlang VM) ที่อยู่เบื้องหลัง
ผลการเรียนรู้:
- สร้างและเรียกใช้ฟังก์ชันที่ไม่มีชื่อ (anonymous functions) ทั้งแบบปกติและแบบแคปเจอร์ (
&) - ใช้การจับคู่รูปแบบ (pattern matching) และการเรียกซ้ำ (recursion) ภายในโมดูลเพื่อจัดการตรรกะที่ซับซ้อนโดยใช้ฟังก์ชันที่มีชื่อ
- ใช้เงื่อนไขควบคุม (guard clauses) และพารามิเตอร์เริ่มต้น (default parameters) เพื่อควบคุมลำดับการทำงานของฟังก์ชัน
🔹 บทที่ 4: รายการแบบเรียกซ้ำและโครงสร้างข้อมูล
ภาพรวม: บทนี้ครอบคลุมกลไกพื้นฐานของรายการในภาษา Elixir ผ่านการเรียกซ้ำ โดยเฉพาะอย่างยิ่งเน้นรูปแบบ "หัว-ก้อย" (Head and Tail) เพื่อประมวลผลและสร้างข้อมูล บทนี้ยังขยายไปสู่โครงสร้างข้อมูลที่ซับซ้อน—แมป (Maps), โครงสร้าง (Structs), และรายการคำสำคัญ (Keyword Lists)—ให้กรอบการตัดสินใจในการเลือกโครงสร้างที่เหมาะสม และเทคนิคขั้นสูงในการจัดการข้อมูลซ้อนกันโดยใช้โมดูล Access และทฤษฎีระบบประเภทของภาษา Elixir
ผลการเรียนรู้:
- แยกโครงสร้างและสร้างรายการ: ใช้การจับคู่หัว/ก้อย (head/tail pattern matching) เพื่อสำรวจและสร้างโครงสร้างรายการแบบเรียกซ้ำ
- นำไปใช้รูปแบบระดับสูง: สร้างฟังก์ชัน
mapและreduceที่กำหนดเองเพื่อเปลี่ยนแปลงหรือรวมข้อมูลในรายการ - เลือกโครงสร้างข้อมูลที่เหมาะสม: แยกแยะระหว่างแมป (Maps), โครงสร้าง (Structs), และรายการคำสำคัญ (Keyword Lists) ตามความต้องการด้านประสิทธิภาพ ลำดับ และความสมบูรณ์ของข้อมูล
🔹 บทที่ 5: รายการที่สามารถวนซ้ำได้ (Enumerables), สตรีม (Streams), และการประมวลผลสายอักขระ
ภาพรวม: บทนี้สำรวจแนวทางสองแบบในการประมวลผลคอลเลกชันในภาษา Elixir: โมดูล Enum ที่ทำงานทันที (greedy) และโมดูล Stream ที่ทำงานแบบล่าช้า (lazy) และสามารถประกอบกันได้ บทนี้ยังให้การเจาะลึกเรื่องการเปลี่ยนแปลงข้อมูลผ่านการเขียนแบบรวม (comprehensions) และกลไกภายในของสายอักขระในภาษา Elixir โดยแยกแยะระหว่างสายอักขระที่ใช้เครื่องหมายคู่ (single-quoted strings = character lists) และสายอักขระที่ใช้เครื่องหมายคู่ (double-quoted strings = binaries) นักเรียนจะได้เรียนรู้การประมวลผลโครงสร้างข้อมูลซับซ้อน การจัดการข้อมูลอนันต์ และการดึงข้อมูลระดับบิต
ผลการเรียนรู้:
- แยกแยะระหว่างการประเมินแบบทันที (greedy evaluation) และแบบล่าช้า (lazy evaluation) เมื่อประมวลผลคอลเลกชัน
- ใช้การเขียนแบบรวม (list comprehensions) พร้อมตัวสร้างหลายตัวและตัวกรองเพื่อเปลี่ยนแปลงข้อมูลและดึงข้อมูลระดับบิต
- แยกแยะระหว่างสายอักขระที่ใช้เครื่องหมายคู่ (single-quoted strings = character lists) และสายอักขระที่ใช้เครื่องหมายคู่ (double-quoted strings = binaries) และใช้โมดูลที่ถูกต้อง (List vs. String) ในการจัดการ
🔹 บทที่ 6: การควบคุมการไหล, โปรเจกต์ Mix และเครื่องมือมืออาชีพ
ภาพรวม: บทนี้นำนักพัฒนาจากงานเขียนฟังก์ชันภาษา Elixir ที่โดดเดี่ยว มาสู่การสร้าง ทดสอบ และตรวจสอบแอปพลิเคชันระดับมืออาชีพ ครอบคลุมโครงสร้างควบคุมขั้นสูง (case, cond) วงจรชีวิตของโปรเจกต์ Mix (ตั้งแต่โครงสร้างโฟลเดอร์ไปจนถึงไฟล์รันจากไลน์คำสั่ง) และชุดเครื่องมือมืออาชีพสำหรับการแก้ไขบั๊ก การทดสอบแบบพื้นฐานคุณสมบัติ (property-based testing) และการตรวจสอบเซิร์ฟเวอร์
ผลการเรียนรู้:
- ใช้
case,condและการจัดการข้อผิดพลาด (exception handling) เพื่อสร้างตรรกะการแยกทางที่ซับซ้อน - จัดโครงสร้างโปรเจกต์ภาษา Elixir โดยใช้
Mixจัดการการพึ่งพาภายนอก เช่นHTTPoisonและPoisonและตั้งค่าสภาพแวดล้อมของแอปพลิเคชัน - พัฒนาชุดการทดสอบที่แข็งแรงโดยใช้
ExUnit,DocTestและการทดสอบแบบพื้นฐานคุณสมบัติ (property-based testing) ผ่านStreamData
🔹 บทที่ 7: การประมวลผลขนานและโหนดกระจาย
ภาพรวม: บทนี้สำรวจการเปลี่ยนจากแอปพลิเคชันภาษา Elixir ที่ทำงานในกระบวนการเดียว มาเป็นระบบกระจาย ครอบคลุมกลไกการจัดการข้อความ การคงอยู่ของกระบวนการผ่านลูปแบบเรียกซ้ำท้าย (tail-recursive loops) และการจัดการชีวิตของกระบวนการอย่างมั่นคงผ่านการเชื่อมโยง (links) และการเฝ้าระวัง (monitors) สุดท้ายบทนี้นำเสนอโมเดลการกระจายของเครื่องเสมือนเออร์แลง (Erlang VM) สอนวิธีการเชื่อมต่อโหนด ป้องกันด้วยคุกกี้ (cookies) และจัดการการรับส่งข้อมูล (I/O) ผ่านเครือข่าย
ผลการเรียนรู้:
- สร้างกระบวนการที่มีสถานะและคงอยู่โดยใช้การเรียกซ้ำท้าย (tail recursion) และเวลาหมด (message timeouts)
- สร้างต้นไม้กระบวนการที่ทนต่อข้อผิดพลาดโดยใช้การเชื่อมโยง (
spawn_link) และการเฝ้าระวัง (spawn_monitor) - ตั้งค่าและเชื่อมต่อโหนดที่กระจายโดยใช้ชื่อที่ตั้งไว้ คุกกี้ด้านความปลอดภัย และการลงทะเบียนกระบวนการทั่วโลก
🔹 บทที่ 8: พื้นฐานของ OTP: เซิร์ฟเวอร์และซูเปอร์ไวเซอร์
ภาพรวม: บทนี้แนะนำเฟรมเวิร์ก Open Telecom Platform (OTP) ภายในระบบนิเวศของภาษา Elixir โดยเฉพาะอย่างยิ่งเน้นพฤติกรรม GenServer และรูปแบบ Supervisor นักเรียนจะได้เรียนรู้วิธีสร้างกระบวนการเซิร์ฟเวอร์ที่มั่นคงและมีสถานะ แยกแยะระหว่างการสื่อสารแบบสังเกต (สังเกตเห็น) และแบบไม่สังเกต (สังเกตเห็น) และตั้งค่าต้นไม้ซูเปอร์ไวเซอร์ที่ทนต่อข้อผิดพลาด ซึ่งจัดการชีวิตของกระบวนการโดยอัตโนมัติ
ผลการเรียนรู้:
- กำหนดองค์ประกอบหลักของ OTP และใช้ชีวิตของ callback แบบมาตรฐาน
GenServer - แยกแยะและใช้รูปแบบการสื่อสารแบบสังเกต (call) และแบบไม่สังเกต (cast) ได้อย่างถูกต้อง
- ตั้งค่าและใช้งาน
Supervisorเพื่อเฝ้าระวังกระบวนการงาน (worker processes) และรักษาความน่าเชื่อถือของระบบเมื่อเกิดข้อผิดพลาด
🔹 บทที่ 9: สถาปัตยกรรม OTP ที่ซับซ้อนและการจัดการสถานะ
ภาพรวม: บทนี้เปลี่ยนจาก GenServer รายบุคคล มาสู่การวางแผนและเผยแพร่แอปพลิเคชัน OTP ที่ซับซ้อนและมีหลายส่วน ครอบคลุมการออกแบบสถาปัตยกรรมแอปพลิเคชัน "ดูเปอร์" (Duper) ที่ตรวจจับไฟล์ซ้ำ การจัดการเฉพาะเจาะจงของแอปพลิเคชัน OTP และเทคนิคการเผยแพร่ขั้นสูง เช่น การอัปเกรดแบบสด (hot upgrades) โดยใช้ Distillery นอกจากนี้ยังสำรวจทางเลือกที่ง่ายกว่าในการจัดการสถานะ เช่น Tasks และ Agents พร้อมกรอบการเลือกเครื่องมือที่เหมาะสมตามความต้องการด้านการประมวลผลขนาน
ผลการเรียนรู้:
- วิเคราะห์ความต้องการของแอปพลิเคชันโดยใช้กรอบคำถาม 5 ข้อ เพื่อระบุจุดสำคัญและลักษณะการทำงาน
- สร้างแอปพลิเคชัน OTP หลายเซิร์ฟเวอร์ (Duper) โดยใช้เซิร์ฟเวอร์เฉพาะ (Results, PathFinder, Gatherer) และ
Dynamic Supervisors - ดำเนินการปล่อยโค้ดและอัปเกรดแบบสดโดยใช้ Distillery รวมถึงการย้ายสถานะผ่าน
code_changecallback
🔹 บทที่ 10: การเขียนโปรแกรมเชิงเมตา โปรโตคอล และความปลอดภัยของประเภท
ภาพรวม: บทนี้สำรวจความสามารถในการขยายระบบขั้นสูงของภาษา Elixir โดยเน้นการจัดการโค้ดเป็นข้อมูลผ่านการเขียนโปรแกรมเชิงเมตา (metaprogramming) และแมโคร (macros) นักเรียนจะได้เรียนรู้การบรรลุการเป็นพหุรูป (polymorphism) ผ่านโปรโตคอล (Protocols) และพฤติกรรม (Behaviours) จัดระเบียบระบบขนาดใหญ่ด้วยโปรเจกต์แบบอัมเบรลลา (Umbrella projects) และจัดการข้อผิดพลาดอย่างมั่นคง สุดท้ายบทนี้ครอบคลุมการเพิ่มชั้นการวิเคราะห์แบบสถิตโดยใช้ระบบประเภทของภาษา Elixir และ Dialyzer เพื่อให้มั่นใจในความถูกต้องของโค้ด
ผลการเรียนรู้:
- ใช้
quoteและunquoteได้อย่างคล่องแคล่วเพื่อแทรกและจัดการบล็อกโค้ดภายในแมโคร - สร้างโปรโตคอลและพฤติกรรมที่กำหนดเองเพื่อสร้างโครงสร้างโค้ดที่มีลักษณะพหุรูปและใช้ซ้ำได้
- สร้างโปรเจกต์อัมเบรลลาแบบหลายแอปพลิเคชัน และใช้คำอธิบายประเภท (Type Specifications) กับโค้ดแบบไดนามิกของภาษา Elixir