November 2, 2026
เริ่มต้นกับ Functional Programming
ก่อนหน้านี้ผมเจอโพสต์หนึ่งถามว่า สำหรับคนที่เคยใช้แต่ OOP จะเริ่มต้นกับ Functional Programming ควรเริ่มยังไงดี

By Chokchai Phatharamalai
2 min read
ผมชอบคำตอบที่ตัวเองตอบไป เลยอยากเอามาจดเก็บไว้
ฝันร้ายของโปรแกรมเมอร์
ก่อนจะเข้าไปใน programming แต่ละ paradigm ผมอยากเริ่มต้นที่ปัญหาก่อน
หนึ่งในปัญหาที่หลอกหลอนโปรแกรมเมอร์มากที่สุด คือ บั๊กบน production ที่เกิดจาก shared mutable state โดยเฉพาะ global variable เพราะ global variable คือตัวแปรที่โค้ดหลายส่วนของโปรแกรมสามารถเข้าถึงและแก้ไขได้ นั่นแปลว่า ถ้าค่าของมันผิดขึ้นมา โค้ดที่เป็นผู้ต้องสงสัยอาจกระจายอยู่ทั่วทั้งระบบ
โปรแกรมเมอร์ก็หาเป็นไก่ตาแตกเลยว่า ใครนะที่แก้ตัวแปรนี้ผิดไป ทำให้ระบบทำงานผิดพลาด แล้วภายใต้แรงกดดันที่ผู้ใช้งานกำลังเร่งให้ระบบกลับมาทำงานได้ตามปกติ เค้าจะได้ทำงานของเค้าต่อได้ มันช่างกดดันเหลือเกิน
ถ้ามองผ่านปัญหา shared mutable state นี้ OOP และ FP เป็นสองแนวทางที่จัดการกับปัญหาคล้าย ๆ กัน แต่ใช้วิธีต่างกัน
OOP แก้ปัญหายังไง?
ถ้ากลับไปดูภาษาโปรแกรมมิ่งเก่า ๆ อย่าง assembly ซึ่งเป็น imperative programming เราจะเห็นการเขียนโปรแกรมในรูปแบบชุดคำสั่งเพื่อสั่งงานเครื่องคอมพิวเตอร์ เช่น เอาตัวเลขนี้ไปเก็บในช่องนั้น ย้ายค่าจากช่องนั้นไปช่องนี้ หรือเอาค่าจาก 2 ช่องมาบวกกัน เป็นต้น
เมื่อโปรแกรมใหญ่ขึ้น ถ้า state ต่าง ๆ ถูกเปิดให้โค้ดทุกส่วนเข้าถึงและแก้ไขได้ การดูแลโปรแกรมก็จะยากขึ้นเรื่อย ๆ
หนึ่งในแนวคิดสำคัญของ Object-Oriented Programming คือการเอา state กับ behavior ที่เกี่ยวข้องกันมาอยู่ในขอบเขตเดียวกัน แล้วซ่อนรายละเอียดภายในเอาไว้
เช่น ถ้าเรามี Object Student ข้อมูลต่าง ๆ ของ Student ไม่ว่าจะเป็นชื่อ เกรดเฉลี่ย หรือชั้นปี ก็อยู่กับ Student และพวกฟังก์ชันที่ใช้จัดการข้อมูลเหล่านี้ก็อยู่กับ Student เช่นกัน เราเรียกฟังก์ชันเหล่านี้ว่า method
แทนที่โค้ดทั้งระบบจะเข้ามาแก้ข้อมูลของ Student โดยตรง เราสามารถกำหนดได้ว่าใครเห็นอะไรได้ และจะเปลี่ยน state ผ่านทางไหน
พอเราจำกัด scope และซ่อน state ไว้ ถ้า Student มีค่าอะไรบางอย่างผิดพลาด ผู้ต้องสงสัยก็แคบลง เพราะโค้ดภายนอกไม่สามารถเข้ามาแก้ state ภายในได้ตามใจ
พูดง่าย ๆ คือ OOP ไม่ได้พยายามกำจัด state ที่เปลี่ยนแปลงได้ แต่พยายามควบคุมว่า ใครมีสิทธิ์เข้าถึงและเปลี่ยน state นั้น
นอกจาก scope แล้ว Object ยังมี life cycle ของตัวเองด้วย เมื่อ Object นั้นไม่ถูกใช้งานแล้ว ระบบก็สามารถจัดการ memory ที่เกี่ยวข้องกับมันได้
FP แก้ปัญหายังไง?
ถ้า OOP พยายามควบคุมว่าใครแก้ state ได้บ้าง Functional Programming ไปอีกทาง คือพยายามลดการแก้ state ลงให้มากที่สุดตั้งแต่แรก
ถ้าจะพูดเล่น ๆ แบบสุดโต่งหน่อย คือ
OOP พยายามแบน global FP พยายามแบนการเปลี่ยนค่า variable
Functional Programming ให้ความสำคัญกับ immutability คือ เมื่อข้อมูลถูกสร้างขึ้นมาแล้ว เราพยายามไม่แก้ค่าของมัน แต่สร้างค่าใหม่จากค่าเดิมแทน
เช่น ถ้ามี
x = 1x = 1แทนที่จะเปลี่ยน x จาก 1 เป็น 2 เราจะสร้างค่าใหม่ขึ้นมา
x = 1
y = x + 1x = 1
y = x + 1x ก็ยังคงเป็น 1 เหมือนเดิม
อีกแนวคิดสำคัญคือ pure function ซึ่งเป็น function ที่ผลลัพธ์ขึ้นอยู่กับ input ที่รับเข้ามา ถ้า input เหมือนเดิม ก็ควรให้ output เหมือนเดิม และไม่แอบไปเปลี่ยนแปลง state ข้างนอก function
ลองนึกภาพว่าเรามี
input
↓
function
↓
outputinput
↓
function
↓
outputสิ่งที่เกิดขึ้นใน function สามารถทำความเข้าใจได้จาก input และ output ของมัน โดยไม่ต้องตามไปดูว่า global variable ตัวไหนเพิ่งถูกแก้มาก่อนหน้านี้
ถ้าระหว่างทางต้องคำนวณหรือเก็บค่าใหม่ เราก็สร้างค่าใหม่ขึ้นมา แทนที่จะกลับไปแก้ค่าเดิม
ผลที่ตามมาคือ code ส่วนหนึ่งของเราจะ predictable มากขึ้น และเหตุผลเกี่ยวกับมันได้ง่ายขึ้น
FP กับการย้อนเวลา
ผลพลอยได้ที่น่าสนใจมากของ immutable data คือ มันทำให้การย้อนกลับไปดู state ในอดีตง่ายขึ้นมาก
สมมติว่าโปรแกรมมี state แบบนี้
state1
↓
state2
↓
state3state1
↓
state2
↓
state3ถ้าเราใช้ mutable state เราอาจมี Object เดียวที่ถูกแก้ไปเรื่อย ๆ
state
↓ mutate
state
↓ mutate
statestate
↓ mutate
state
↓ mutate
stateค่าเก่าถูกเขียนทับไปแล้ว ถ้าอยากรู้ว่าเมื่อก่อนมันเป็นอะไร เราต้องมี mechanism เพิ่ม เช่น log, snapshot หรือ undo history
แต่ถ้าเราใช้ immutable data เราสามารถสร้าง state ใหม่จาก state เดิมได้
state1 = ...
state2 = update(state1)
state3 = update(state2)state1 = ...
state2 = update(state1)
state3 = update(state2)โดย state1 และ state2 ยังอยู่เหมือนเดิม
ดังนั้นถ้าเราอยากย้อนกลับไปดูว่า ตอนนั้นโปรแกรมอยู่ใน state ไหน เราก็สามารถกลับไปดู state เก่าได้
นี่คือเหตุผลว่าทำไมระบบที่ใช้แนวคิด Functional Programming บางประเภทถึงทำสิ่งอย่าง time-travel debugging ได้ง่าย
มันไม่ได้ย้อน program pointer กลับไปในอดีตจริง ๆ แต่มันสามารถย้อนกลับไปดู state ในอดีต ได้ เพราะ state เก่าไม่ได้ถูกทำลายทิ้ง
ทำไมผมถึงไม่กลัว function ยาวใน FP เท่า method ยาวใน OOP
ความเรียบง่ายแบบนี้ทำให้ผมไม่กลัว function ยาว ๆ ใน FP เท่ากับ method ยาว ๆ ที่เต็มไปด้วย mutable state
ไม่ใช่ว่า function ยาวเป็นเรื่องดีนะ ยาวก็ควรแยกอยู่ดี
แต่ถ้า function หนึ่งเป็น pure function ผมสามารถดูมันแบบ
input → transformation → outputinput → transformation → outputแล้วพยายามทำความเข้าใจมันได้โดยไม่ต้องกังวลมากนักว่า ระหว่างทางมันไปเปลี่ยน state อะไรข้างนอกหรือเปล่า
ในทางกลับกัน method ยาว ๆ ที่เปลี่ยน state ของ Object ไปเรื่อย ๆ จะทำให้ผมต้องจำตลอดว่า ตอนนี้ค่าของแต่ละ field เปลี่ยนไปเป็นอะไรแล้ว
อันนั้นสำหรับผมน่าสยดสยองกว่าเยอะ
ทำไม FP ถึงยากสำหรับคนที่ชินกับ Imperative Programming
สิ่งหนึ่งที่ทำให้ผมรู้สึกว่า FP ยากในตอนเริ่มต้น คือ วิธีคิดของมันต่างจาก imperative programming พอสมควร
เวลาเขียน imperative เรามักจะคิดเป็นขั้นตอนว่า
ทำ A
แก้ค่า B
วน loop
ถ้าเป็นแบบนี้ให้แก้ C
แล้วค่อยไปทำ Dทำ A
แก้ค่า B
วน loop
ถ้าเป็นแบบนี้ให้แก้ C
แล้วค่อยไปทำ Dแต่เวลาเขียน FP ผมมักจะคิดในรูปของการเปลี่ยนข้อมูลจากรูปหนึ่งไปอีกรูปหนึ่งมากกว่า
เช่น ถ้าผมมีคำตอบของข้อสอบทุกวิชาของนักเรียนคนหนึ่ง แล้วอยากหาเกรดเฉลี่ย ผมอาจเริ่มถามตัวเองว่า
Q: ก่อนจะได้เกรดเฉลี่ย จะต้องมีอะไรก่อน? A: เกรดทุกวิชา
ผมก็จะรู้ว่า function สุดท้ายที่ return เกรดเฉลี่ย จะต้องรับ input เป็นเกรดของทุกวิชา
แล้วถามต่อว่า
Q: ก่อนจะได้เกรดทุกวิชา ต้องมีอะไรก่อน? A: คะแนนสอบของแต่ละวิชา
ก็จะได้ transformation ประมาณนี้
คำตอบของข้อสอบ
↓
คะแนนสอบ
↓
เกรด
↓
เกรดเฉลี่ยคำตอบของข้อสอบ
↓
คะแนนสอบ
↓
เกรด
↓
เกรดเฉลี่ยสุดท้ายโปรแกรมก็จะกลายเป็นการต่อ function เล็ก ๆ เข้าด้วยกัน
input
→ transform
→ transform
→ transform
→ outputinput
→ transform
→ transform
→ transform
→ outputวิธีคิดแบบนี้ไม่ใช่ว่า imperative programming ทำไม่ได้ หรือ FP ต้องคิดถอยหลังเสมอไป
แต่มันเป็นวิธีคิดแบบ data transformation และ function composition ซึ่งผมเจอบ่อยมากเวลาเขียน Functional Programming และสำหรับคนที่ชินกับ imperative programming อาจต้องใช้เวลาปรับวิธีคิดอยู่บ้าง
ทำไมควรรู้ทั้ง FP และ OOP
วิธีการแก้ปัญหาของ 2 paradigm นี้ มันเหมือนค้อนกับกรรไกร บางงานมันแก้ด้วยเครื่องมืออีกอย่างง่ายกว่ามาก ๆ ถ้าเรารู้จักทั้ง 2 เครื่องมือ เราก็จะเลือกใช้ได้อย่างเหมาะสม
OOP พยายามทำให้ state ที่เปลี่ยนแปลงได้อยู่ในขอบเขตที่ควบคุมได้
FP พยายามลดจำนวน state ที่เปลี่ยนแปลงได้ลงตั้งแต่แรก
ทั้งสองแนวทางไม่ได้เป็นศัตรูกันด้วย ทุกวันนี้ภาษาโปรแกรมจำนวนมากก็เอาแนวคิดจากทั้ง OOP และ FP มาใช้ร่วมกัน
ผมเคยเห็นโค้ด imperative เป็นหลายพันบรรทัด ที่พอเปลี่ยนมามองปัญหาเป็น data transformation แล้ว ใช้แนวคิดแบบ FP กลับเหลือโค้ดไม่ถึง 200 บรรทัดก็มี
ที่ผมเลือกเอาเรื่องนี้มาแบ่งปัน เพราะในชีวิตเราอาจจะเจอปัญหาที่บางครั้งเราคิดว่ายากจังเลย
ผมอยากฝากคำของ Gerald Weinberg ไว้ว่า
ปัญหาไม่มีง่ายหรือยาก ยากหรือง่ายมันอยู่ที่ solution ในการแก้ปัญหา ปัญหาที่เราคิดว่ายาก คือปัญหาที่เรายังไม่รู้ solution ง่าย ๆ