FP(00) – บทนำ, ก้าวแรกสู่โลกแห่ง Functional Programming

developer

บทความชุด: Functional Programming

รวมเนื้อหาเกี่ยวกับการเขียนโปรแกรมแนว Functional และหัวข้ออื่นๆ ที่เกี่ยวข้อง

- FP 00: บทนำ, ก้าวแรกสู่โลกแห่ง Functional Programming
- FP 01: Pure, First-Class, High-Order, พื้นฐานแห่ง Functional Programming
- FP 02: map, filter, reduce และผองเพื่อน, อาวุธหนักแห่ง Functional Programming
- FP 03: Recursive Function, เขียนลูปไม่ได้ทำยังไง มาเขียนฟังก์ชันเรียกตัวเองแทนกันเถอะ
- FP 04: โครสร้างแบบ Pair และ Either กับ List Comprehension การสร้างลิสต์ฉบับฟังก์ชันนอล
- FP 05: Immutable, Curry, Partial Function, Lazy Evaluation
- FP 06: Functor and Monad
- [บทความปี 2015] มารู้จักกับ Functional Programming สิ่งที่คุณต้องรู้ในตอนนี้กันเถอะ
- Function ทำงานยังไง?, ในมุมมองของโปรแกรมเมอร์สาย Imperative

Category Theory


Lambda Calculus

Prologue: เกริ่นนำซีรีส์ All About FP

ในช่วงหลายๆ ปีที่ผ่านมา เชื่อว่าหลายๆ คนคงเคยได้ยินคำว่า "Functional Programming" ผ่านหูกันมาบ้าง แต่อาจจะยังมีความสงสัยว่ามันคืออะไร ต่างจากการเขียนโปรแกรมแบบปกติที่เคยเขียนๆ มาแค่ไหน แล้วเวลาที่เราสร้าง function ในภาษาโปรแกรมปกติที่เคยทำกันมามันเหมือนหรือไม่เหมือนกับคอนเซ็ปของ Functional Programming ยังไง ... นี่ยังอ่าน Object-Orient Programming ไม่จบเลยนะ จะมีของใหม่มาอีกแล้วเหรอ?

ในบทความชุดนี้เราจะมาเฉลยคำตอบพวกนั้น โดยเราจะได้รู้จักกับตัวละครใหม่ นอกจากเหนือคำสั่งที่เคยใช้ๆ อยู่ทุกวันอย่าง if-else หรือการเขียน loop, หรือหากว่าคุณมาจากสาย Object-Oriented (OOP) คุณก็จะได้รู้จักกับเพื่อนใหม่นอกเหนือจาก class, object, extends และ interface กันบ้าง

** หลังจากนี้จะขอเรียก Functional Programming ย่อว่า FP นะ

TL;DR บทความนี้จะเป็นการเกริ่นนำถึงที่มาที่ไปของ FP กันก่อน .. ถ้าใครอยากข้ามช่วงประวัติศาสตร์สามารถข้ามไปอ่านบทที่ 1 ได้เลย

การเขียนโปรแกรมตามปกติของเรา จะเป็นการนำคำสั่งต่างๆ มาเรียงต่อกัน หรือที่เราเรียกว่า Algorithm นั่นเอง โดยอาจจะใช้ตัวควบคุมทิศทางของโปรแกรม (Flow Control) เช่น if-else หรือ loop มาช่วยด้วย

Quick Start!

ถ้าจะให้บอกแบบรวบรัดว่าภาษาสไตล์ Functional ต่างจากภาษาโปรแกรมทั่วไปที่เราเขียนยังไงบ้าง

  1. ทุกอย่างสามารถกลายเป็น function ได้ทั้งหมด
  2. การคิด Algorithm จะไม่ได้อยู่ในรูปแบบของการนำคำสั่งมาเรียงต่อๆ กันแล้ว แต่เป็นการสร้างส่วนคำนวณขึ้นมาแทน (เปลี่ยนจากการ Command -> Compute แทน)
  3. เรื่องน่าแปลกใจสุดๆ คือเราไม่สามารถเปลี่ยนแปลงค่าตัวแปรได้! (Immutable) ลองนึกถึงการเขียนโปรแกรมที่ตัวแปรทุกตัวเซ็ตค่าได้ครั้งเดียว แล้วเปลี่ยนค่าไม่ได้อีกเลยดูสิ!
  4. และก็ ไม่!! อีกนั่นแหละ, ไม่มี loop ให้ใช้งานในภาษาสไตล์นี้ (เพราะเราเปลี่ยนค่าตัวแปรที่เอาไว้นับลูปอย่าง i ไม่ได้)
  5. จะมีคำศัพท์ไม่คุ้นเคย เช่น Immutable, Lazy Evaluation, Functor, Monad

สำหรับภาษาโปรแกรม (Programming Language) เราสามารถออกเป็น 2 กลุ่มตามสไตล์การเขียน คือ

Imperative

เป็นการบอกขั้นตอนอย่างละเอียด โฟกัสที่ "How" สอนคอมพิวเตอร์ทุกขั้นตอน เป็นสไตล์การเขียนของภาษาโปรแกรมตัวดังๆ ที่ใช้กันอยู่ในท้องตลาด ก็พวก C/C++, Java, Python, JavaScript, PHP, Go ที่พูดไปข้างต้น

สไตล์การเขียนแบบนี้เหมือนเป็นสายลุยครับ อยากได้อะไรต้องลุยเองทั้งหมด เน้นการใช้ กำหนดค่าตัวแปร โดยใช้ if-else และ loop ในการควบคุมโปรแกรม

สไตล์การเขียนโปรแกรมในกลุ่มนี้คือการเขียนโปรแกรมที่เราคุ้นเคยกัน Structure-Procedural Programming และ Object Oriented Programming

Declarative

เป็นการบอกคอมพิวเตอร์ว่าเราต้องการอะไร กำหนด/นิยามข้อกำหนดต่างๆ โดยไม่บอกขั้นตอนว่าต้องทำอย่างไร โฟกัสที่ "What" ว่าอยากได้อะไร แล้วให้คอมพิวเตอร์ไปคิดเอาเองว่ามันต้องคำนวณยังไงให้ได้ "What" ที่เราอยากได้ออกมา

Functional Programming นั้นอยู่ในสไตล์แบบนี้นี่เอง (อีกประเภทที่อยู่ในกลุ่มนี้คือภาษากลุ่ม Logic และ SQL)


เพื่อให้เห็นภาพ
ลองมาดูตัวอย่างกัน ข้างล่างนี่เป็นโค้ด การเพิ่มค่าทุกตัวในอะเรย์ด้วย 1 ซึ่งเราเขียนด้วยการวนลูปแบบธรรมดา

function increment_one(arr){
    for(i = 0; i < arr.length; i++){
        arr[i] = arr[i] + 1
    }
}

จะเห็นว่าเวลาเราเขียนโปรแกรม เราจะทำการ เปลี่ยนค่าตัวแปร ตามอัลกอริทึมที่สร้างขึ้นมาจนได้ผลลัพธ์ออกมา
โค้ดประมาณนี้เป็นโค้ดที่เราเขียนกันอยู่ตลอดเวลาจนอาจจะทำให้มองข้ามอะไรบางอย่างไปได้คือ พอเราเขียนโปรแกรมมานานระดับหนึ่งแล้ว เราจะเคยชินกับการแปลงหลักการคิดให้อยู่ในรูปของ คำสั่งและการเปลี่ยนแปลงค่าตัวแปร แทนที่จะโฟกัสกับการทำงานจริงๆ

ลองมาเปลี่ยนมุมมอง ลืมๆ การเขียนโปรแกรมแบบเดิมไปก่อน แล้วให้ลองคิดว่าถ้าเราต้องการจะเพิ่มค่า 1 ให้กับไอเทมทุกตัวของอะเรย์ แต่พูดเป็นภาษาคนแทน (หรือให้ใกล้เคียงภาษาคนให้ได้มากที่สุด)

มาลองคิดกัน!
เราต้องการจะสร้างฟังก์ชันสำหรับเพิ่มค่าทุกตัวในอะเรย์ ขอตั้งชื่อว่า increment_one ในขึ้นแรกต้องกำหนด (define) ขึ้นมาก่อนว่าฟังก์ชันนี้จะรับ [Int] (array of Int) เข้ามาคำนวณแล้วตอบค่ากลับเป็น [Int] อีกชุดหนึ่ง

increment_one :: [Int] -> [Int]

จากนั้นเราก็ค่อยๆ มากำหนดว่าเจ้าฟังก์ชันนี้ ถ้ารับค่าแบบไหนเข้ามา จะให้คำนวณยังไง
เช่น กรณีที่ง่ายที่สุดคือถ้า input เข้ามาเป็นอะเรย์ว่าง (empty array) --> ก็ไม่ต้องทำอะไร สามารถตอบกลับไปได้เลย (เพราะไม่มีอะไรจะให้บวก)

increment_one [] = []

ต่อมา...
ในกรณีที่มีไอเทมในอะเรย์มีการกว่าหนึ่งตัว ถ้าพูดเป็นภาษาคน เช่นกำลังพูดกับเพื่อน ก็จะพูดได้ว่า "ให้เอาตัวเลขตัวแรกไปบวก 1 นะ ส่วนตัวต่อๆ ไปในอะเรย์ก็ทำเหมือนๆ กันทุกตัวเลยนะ"
ซึ่งสามารถเขียนเป็นโค้ดได้ประมาณนี้

increment_one (first:other) = first + 1 : increment_one other

อธิบาย --> ฟังก์ชัน increment_one นั้นจะรับ list (หรือ array นั่นแหละ) เข้ามา ซึ่งเราขอแบ่งลิสต์ตัวนี้ออกเป็น 2 ฝั่ง นั่นคือ ตัวแรก และ ตัวที่เหลือทั้งหมด
จากนั้นบอกว่า เอาตัวแรกไป +1 แล้วเอามาต่อกับส่วนที่สองที่นำไป increment_one เช่นกัน

พอเอาทั้งหมดมารวมกันเราจะได้โค้ดหน้าตาแบบนี้ออกมา

increment_one :: [Int] -> [Int]
increment_one [] = []
increment_one (first:other) = first + 1 : increment_one other

จะเห็นว่าแนวคิดในการเขียนโปรแกรมแนวนี้จะต่างจากการใช้งาน loop ที่เราคุ้นชินกัน ยิ่งใครชินกับการเขียนโปรแกรมแบบ imperative มานานๆ แล้วอาจจะดูขัดตา ไม่ชินมากๆ

แล้วตอนนี้เราอยู่ตรงไหน?

หากนำเนื้อหาทั้งหมดมาวางเรียงกันโดยให้ทางซ้ายสุดเป็นฝั่ง คณิตศาสตร์ (แน่นอนว่าหลักการทางวิทยาศาสตร์-คอมพิวเตอร์หลายๆ อย่างได้มาจากหลักการทางคณิตศาสตร์) ส่วนฝั่งขวาสุดเป็นฝั่งการเขียนโปรแกรมแบบที่เราคุ้นเคยดีคือ Imperative Programming ซึ่งเป็นการเขียนโปรแกรมโดยการเอาคำสั่งต่างๆ มาเรียงต่อกัน

เราจะได้ชาร์ตหน้าตาประมาณนี้ออกมา

จะเห็นว่าโซนที่เราคุ้นเคยกันจะอยู่ทางขวาสุด เป็นการเขียนโปรแกรมแบบดั้งเดิมที่เราเรียนๆ กันมา ไม่ว่าจะเป็นการกำหนดค่าให้ตัวแปร มีการใช้คำสั่งควบคุมทิศทางของโปรแกรมเช่น if หรือ loop การใช้สร้าง struct หรือ class

ถ้าเราเลื่อนกลับมาทางซ้ายขึ้นเรื่อยๆ เราจะพบกับคอนเซ็ปของ FP ที่เข้มข้นขึ้นเรื่อยๆ ตัวอย่างเช่นเราอาจจะเคยได้ยินการสร้างฟังก์ชันเป็นตัวแปรที่เรียกว่า First-Class Function หรือแทนที่จะใช้การวนลูปก็เปลี่ยนไปใช้คำสั่ง map หรือ filter แทน คอนเซ็ปพวกนี้มาจากโลกของ FP แต่เอามาปรับๆ ให้เข้ากับโลกของ Imperative

หลังจากนั้นถ้าเรายังเดินต่อไปอีก เราจะเจอกับภาษาสาย FP ที่แท้จริง คอนเซ็ปต่างๆ ที่เคยใช้งานเป็นเรื่องปกติในโลก Imperative จะใช้งานไม่ได้ที่นี่อีกแล้ว

และสุดท้ายถ้าศึกษาไปถึงตรงนั้นแล้วยังอยากรู้ต่อ ก็ต้องข้ามไปยังฝั่งคณิตศาสตร์เพื่อเรียนรู้เรื่องที่เกี่ยวข้องเอาต่อ

ประวัติศาสตร์ของ FP

ในช่วง10ปีที่ผ่านมา (ช่วงปี 2010s) ภาษาดังๆ สาย Imperative มีการหยิบยืมเอาฟีเจอร์หรือการเขียนโปรแกรมสไตล์ FP มาใส่ในภาษาตัวเองเยอะเลย แต่ใส่ลงไปในลักษณ์ของ ส่วนเสริม หรือ Add-on ของภาษาเป็นของเล่นนิดๆ หน่อยๆ เท่านั้น ไม่ใช่ไปแทนที่คำสั่งพวก if-else หรือการใช้ loop ของเดิมเขา เนื่องจากถ้าจะเปลี่ยนแนวคิดหรือสไตล์ของภาษาแล้ว คิดภาษาใหม่ตั้งแต่ต้นยังดีกว่า

ถ้าเราเอาสไตล์การเขียนโปรแกรมแต่ละยุคมาวางเรียงกัน เราจะพบว่า

  1. คอมพิวเตอร์เกิดขึ้นในช่วงยุค 1950s พร้อมกับการกำเนิดของ Machine Code และภาษา Assembly ในยุคนี้ไม่ต้องพูดอะไรมาก โครงสร้างภาษาเป็นยังไงคงยังไม่มีใครสนใจ ขอให้ฉันสั่งงานคอมพิวเตอร์ให้ได้ก่อนนะ
  2. หลังจากนั้นไม่นานก็เป็นยุคของภาษาโปรแกรมระดับสูงภาษาแรกๆ ของโลกนั่นคือ FORTRAN ที่ใช้แนวคิดแบบ Imperative ตามมาด้วย LISP ซึ่งเป็นภาษาสไตล์ FP ภาษาแรกของโลก ... และตามมาอีกด้วย Imperative อีกภาษาคือ COBOL
  3. ในยุค 1970-1980 ก็เป็นยุครุ่งเรื่องของ Imperative Language เช่นภาษา C
  4. ต่อมาก็มีการเอาแนวคิด OOP เสริมเข้ามาอีกที ทำให้ยุค 1990 เป็นต้นมาเป็นยุคทองของ OOP ภาษาไหนเกิดในยุคนี้ ถ้าไม่มีคอนเซ็ป OOP มักจะไม่ดัง ตัวอย่างภาษายอดฮิตก็เช่น Java
  5. จนมาถึงปัจจุบันช่วงปี 2010-2020 เราพบว่ามีการนำคอนเซ็ป FP มาใส่ในทุกภาษาเต็มไปหมด (อย่างที่บอกว่าใส่ในฐานะ ส่วนเสริม เท่านั้น)

จริงๆ แล้วนอกจากช่วงปี 2010-2020 จะมีการนำ FP มาใช้แล้ว อีกแนวคิดหนึ่งที่มาแรงไม่แพงกันคือการเขียนโปรแกรมแบบ Composite Programming เนื่องจากใช่ว่า OOP จะดีเลิศไร้ที่ติ ใช้กันมาตั้ง 20-30 ปีมันก็ต้องมีคนเจอข้อเสียในงานจริงๆ กันบ้างแหละ แต่ Composite คืออะไรไว้มีโอกาสจะยกมาเขียนละกัน ตอนนี้ขอโฟกัสที่ FP กันก่อน

จากไทม์ไลน์นี้ อาจจะทำให้เราหลายคนคิดว่า FP นั้นเป็นของใหม่ เป็นแนวคิดใหม่ที่เพิ่งจะมา แต่จริงๆ แล้วไม่ใช่เลย!

แนวคิดของ FP นั้นมีมาก่อนที่คอมพิวเตอร์จะถูกคิดขึ้นมาซะอีก! เพราะทฤษฎีของ Functional นั้นเกิดในยุค 1930s ส่วนคอมพิวเตอร์เป็นถูกสร้างเป็นเครื่องจักรที่ใช้งานจริงได้เป็นครั้งแรกก็ปี 1945 โน่น (เป็นผลผลิตจากการเกิดสงครามโลกครั้งที่ 2 พอหลังสงครามจบเลยมีการเอามาใช้ในสาขาอื่นนอกจากการทหาร)

เป็นไปได้ยังไง? ที่แนวคิดการเขียนโปรแกรมจะเกิดก่อนคอมพิวเตอร์!

เป็นไปได้ครับ เพราะตอนที่ทฤษฎีของ Functional เกิดขึ้นมามันไม่ได้สร้างขึ้นมาเพื่อนำมาสร้างภาษาคอมพิวเตอร์ไงล่ะ มันถูกสร้างขึ้นมาเชิง Pure Knowledge หรือความรู้สายบริสุทธิ์ (ซึ่งตรงข้ามกับสาย Apply Knowledge ที่เน้นการเอาความรู้มาสร้างอะไรที่ใช้งานได้จริง)

และแนวคิดทางคณิตศาสตร์ที่ว่านั่นก็คือสิ่งที่เรียกว่า Lambda Calculus และ Category Theory

หากสนใจ 2 หัวข้อนี้ อ่านเพิ่มเติมได้ที่
บทความ Lambda Calculus
และบทความ Category Theory

Category Theory เป็นวิชาที่ว่าด้วย mathematical structure ซึ่งมี object มาเชื่อมต่อกันคล้ายทฤษฎีกราฟ ซึ่งเอามาอธิบายและเป็นแนวคิดสำหรับการสร้าง data structure ในการเขียนโปรแกรมได้

ส่วน Lambda Calculus นั้น อย่าเพิ่งตกใจเมื่อเห็นคำว่า Calculus เจ้านี่ไม่ใช่วิชาแคลคูลัสที่เราต้องเรียนในวิชาฟิสิกส์ ใจเย็นๆ

Lambda Calculus ถูกคิดโดย Alonzo Church ซึ่งเป็นนักคณิตศาสตร์ชาวอเมริกัน ชื่อนี้อาจจะไม่เป็นที่รู้จักมากนักสำหรับนักเรียนสายวิทยาการคอมพิวเตอร์ แต่ถ้าบอกว่าเขาคนนี้เป็นอาจารย์ที่ปรึกษาระดับปริญญาเอกของ Alan Turing ผู้ได้รับฉายาว่า "บิดาแห่งวิทยาการคอมพิวเตอร์" หลายๆ คนน่าจะเคยได้ยินชื่อผ่านหูมาบ้าง

คอนเซ็ปของ Lambda Calculus คือสร้างโมเดลในการแก้โจทย์ปัญหาอะไรบ้างอย่าง ที่มีความซับซ้อนมากๆ หรือเป็นปัญหาที่เป็นตัวเลขเยอะๆ โดยการใช้การสร้างนิพจน์ (Expression) ขึ้นมาแล้ว compute ตามวิธีการของฟังก์ชันในการยุบ/ขยาย/รวม Expression อย่างเป็นระบบตามขั้นตอน จนในที่สุดก็จะได้คำตอบออกมา

เกร็ดเล็กน้อยคือ หากใครรู้จัก Alan Turing ก็น่าจะเคยได้ยินสิ่งที่เรียกว่า Turing Machine หรือเครื่องจักรของทัวริ่งด้วย ซึ่งเป็นเครื่องจักรในจินตนาการ ไม่มีอยู่จริง แต่หลักการของมันนั้นถูกเอามาสร้างเป็นเครื่องคอมพิวเตอร์ในเวลาต่อมา ... ซึ่งเราสามารถแปลงนิพจน์ทั้งหมดใน Lambda Calculus ให้กลายเป็น State หรือสถานะใน Turing Machine ได้ทั้งหมดด้วย!

แล้ว FP หายไปไหนมา, ทำไมเพิ่งกลับมาตอนนี้

ความจริงในช่วงเวลาที่ผ่านมาตั้งแต่เครื่องคอมพิวเตอร์ถูกสร้างขึ้น มีภาษาโปรแกรมหลายภาษาถูกสร้างขึ้นมา แน่นอนครับ บางภาษาก็ดัง/ได้รับความนิยม บางภาษาก็ยากต่อการศึกษาหรือเขียนยาก ก็จะไม่ได้รับความนิยมเป็นธรรมดา ซึ่งภาษาแนว FP ก็จัดอยู่ในพวกนี้แหละครับ คือไม่ได้รับความนิยมจากโปรแกรมเมอร์กระแสหลัก เราเคยแทบจะไม่ได้ยินชื่อภาษาโปรแกรมสายนี้เลย แต่ก็ไม่ใช่ว่ามันไม่มีใครสร้างขึ้นมาหรือไม่มีใครใช้เลยนะ แค่ได้รับความนิยมเป็นกลุ่มเล็กๆ เท่านั้น

หากยังจำได้ ภาษาโปรแกรมระดับสูงที่ถูกสร้างขึ้นหลังจากภาษาแรกอย่าง FORTRAN คือภาษา LISP ก็เป็นภาษาแนว FP

;ตัวอย่างของการเขียนฟังก์ชันหา Factorial ในภาษา LISP
(defun fatorial (n)
  (cond
    ((= n 0) 1)
    (t (* n (fatorial (- n 1))))))

หลังจากภาษา LISP ถูกสร้างขึ้นมาไม่กี่ปี ก็มีภาษาโปรแกรมสาย FP เกิดขึ้นมาอีก 2 ตัว นั่นคือภาษา Scheme และก็ภาษา ML

สำหรับ Scheme นั้นจะออกแนวคล้ายกับ LISP นั่นคือใช้ ( ) ซ้อนกันเยอะๆๆๆ ความเห็นส่วนตัวเราคิดว่ามันอ่านค่อนข้างยาก
ผิดกับ ML ที่เปลี่ยนวิธีการเขียนไปอีกแนวนึง แบบนี้...

(*ตัวอย่างของการเขียนฟังก์ชันหา Factorial ในภาษา ML*)
fun fac 0 = 1
  | fac n = n * fac (n - 1)

ภาษาสาย FP ส่วนใหญ่ในปัจจุบันมักจะได้ต้นแบบมาจาก 2 ภาษานี้แหละ

  • ภาษาFPที่ออกแบบตามแนว LISP ก็เช่น Scheme, Clojure
  • ภาษาFPที่ออกแบบตามแนว ML ก็เช่น Haskell, F#, Elm

จะเห็นว่าจริงๆ FP ไม่ได้หายไปไหน แค่มันไม่ใช่กระแสหลักเท่านั้นเอง

แต่เหตุผลจริงๆ ที่ FP ไม่ดังหรือได้รับความนิยมเท่าภาษาสาย Imperative นั่นก็คือ...

คอมพิวเตอร์สร้างขึ้นมาด้วยหลักการของ Imperative !

ภาษาโปรแกรมเป็นแค่แนวคิด แต่การจะเอามันไปใช้จริงๆ ต้องอิงกับเครื่องจักรที่เราสร้างขึ้นมา คอมพิวเตอร์ทุกวันนี้ถูกสร้างขึ้นมาด้วยแนวคิดแบบ Stored-Procedurer ซึ่งได้รับการคิดค้นโดย John von Neumann (ที่เรียกว่า Von Neumann Model, ใครเคยเรียนวิชา Computer Architecture น่าจะคุ้นๆ กับชื่อนี้)

Von Neumann สร้างเครื่องจักรที่เหมือนกับหลักการของ Turing คือเครื่องคอมพิวเตอร์จะถูกแบ่งออกเป็น 2 ส่วนหลักคือส่วนที่เป็นวงจรสำหรับคำนวณ (ต่อมากลายมาเป็น CPU) และอีกส่วนที่เอาไว้เก็บค่าตัวแปร (ก็คือ Main Memory หรือ RAM นั่นเอง)

ด้วยแนวคิดแบบนี้ การสั่งงานคอมพิวเตอร์เลยต้องอยู่ในรูปของคำสั่งเชิง Imperative
เช่น x = y + 10 นั่นคือ

  1. โหลดค่า y จากเมมโมรี่เข้ามายังตัวประมวลผล
  2. คำนวณค่านั้น โดยเอาไป +10
  3. เอาค่าที่คำนวณได้กลับไปเก็บไว้ในเมมโมรี่ตำแหน่ง x

จะเห็นว่าการสั่งงานแบบนี้เป็นลำดับ/ขั้นตอน (เป็น "How" มากกว่า "What") ดังนั้นภาษาโปรแกรมในยุคแรกๆ เลยถูกออกแบบมาเป็น Imperative ซะเป็นส่วนมาก ยิ่งIntelที่เป็นบริษัทขาย CPU ออกแบบ CPU ของตัวเองตามหลักการ Von Neumann แล้วด้วย เสมือนเป็นการสร้างมาตราฐานว่าหากคุณจะสร้างหรือออกแบบ CPU ใหม่นะ คุณก็ต้องออกแบบให้เป็นแนว Stored-Procedurer แบบนี้แหละ ถ้าออกแบบแบบอื่นคนไม่ใช้ ไม่รู้ด้วยนะ (เพราะของฉันติดตลาดไปแล้วไงล่ะ)

พอเป็นแบบนี้ ถึงเราจะสร้างภาษาโปรแกรมแนว FP ออกมาได้ดีขนาดไหน สุดท้ายแล้วเหมือนต้องทำการคอมไพล์ให้กลายเป็น Machine Code เพื่อให้คอมพิวเตอร์เอาไปรัน ยังไงก็ต้องแปลงให้กลับมาเป็นแนว Imperative อยู่ดี (แม้แต่ OOP ตอนคอมไพล์ก็ต้องแปลงเป็น Imperative อยู่ดี) ภาษา FP ยุคแรกๆ จึงไม่ค่อยได้รับความนิยมเพราะทำงานช้ากว่า แถมยังเรียนรู้ยาก เพราะต้องมีความเข้าใจคณิศาสตร์ส่วนหนึ่งด้วย ไม่ใช่อะไรที่ตรงไปตรงมา เหมือนการเอาคำสั่งมาเรียงๆ กันแล้วทำตามนั้นแบบ Imperative

Return of the FP: การกลับมาอีกครั้ง

ต้องบอกว่าพวกเราโปรแกรมเมอร์ในยุคนี้ เกิดมาในยุคที่ดี อะไรๆ ก็พร้อม คอมพิวเตอร์ทำงานได้เร็วมาก (เร็วจนบางครั้งโปรแกรมเมอร์ก็ละเลยเรื่อง Performance ของโปรแกรมตัวเองไปเลย) ภาษาโปรแกรมส่วนใหญ่ก็ถูกออกแบบมาอย่างดี เพราะเรามีภาษาโปรแกรมรุ่นพี่มากมายเป็นตัวอย่างให้เรียนรู้

การที่จะสร้างภาษาโปรแกรมที่โปรแกรมเมอร์ต้องคุมทุกอย่างเอง ต้องคิดทุกอย่างแบบละเอียดจนบางครั้งเขียนโค้ดออกมาได้ก็จริงแต่กลับมาอ่านอีกทีแล้วไม่รู้เรื่องเลยว่าโปรแกรมมีทำอะไร มันไม่ใช่แล้วสำหรับโลกยุคนี้!

เมื่อ Imperative ไม่ตอบโจทย์ แล้วอะไรล่ะที่เราจะเอามาช่วยให้ภาษาโปรแกรมมันเขียนง่ายขึ้นแต่ยังคงความ Readable (หมายถึงอ่านรู้เรื่อง) เอาไว้ได้อยู่?

คำตอบแรกก็คือ OOP ซึ่งมันพิสูจน์ตัวเองแล้วว่ามันก็เวิร์คอยู่นะ แต่มันยังไม่สุด ... ก็เลยเป็นที่มาของคำตอบที่สอง

ใช่แล้ว! Functional Programming ยังไงล่ะ

สรุป

  1. แนวคิดเกิดจากคณิตศาสตร์ที่ชื่อว่า Lambda Calculus รวมกับทฤษฎีเรื่อง Category Theory อีกหน่อย
  2. FP เป็นแนวคิดของภาษาโปรแกรมที่มีมานานแล้วตั้งแต่ยุคคอมพิวเตอร์เพิ่งเกิดขึ้นมา
  3. ไม่ได้รับความนิยมเพราะในระยะแรกทำงานสู้ภาษาแบบ Imperative ไม่ได้ และอีกเหตุผลคือเรียนรู้/เข้าใจยาก
  4. ในปัจจุบันกลับมาฮิตอีกครั้ง เพราะทำให้โปรแกรมมีความ Readable และทำให้บั๊กของโปรแกรมน้อยกว่าการเขียนแบบ Imperative มากๆ รวมถึงการเขียน Testing และเอาไปทำงานแบบ Parallel ก็ง่ายกว่า
Imperative + OOP Functional Programming
ตัวแปรเป็นแบบแก้ไขค่าได้ (mutable) ตัวแปรแก้ไขค่าไม่ได้ (immutable)
ใช้หลักการ "ต้องทำอย่างไร" (How) ใช้หลักการ "อยากได้อะไร" (What)
ไม่สามารถทำ Parallel Programming ได้ (หรือหากต้องการ ก็ต้องเขียนโค้ดแบบแบบพิเศษ) สามารถทำ Parallel Programming ได้
สามารถ (และส่วนใหญ่จะทำกันด้วย) แก้ไขหรืออ่านค่าจาก global variable หรือเชื่อมต่อกับ I/O ตรงๆ ได้ โดยทั่วไปการรันฟังก์ชันใดๆ ก็ต้อง No-Side Effects หรือสงผลกระทบต่อ global variable หรือ I/O ภายนอก
ใช้ Loop เป็นเรื่องปกติ ไม่มี Loop จะใช้การเรียกฟังก์ชันแบบ Recursive แทน
การคำนวณ (Evaluate) อะไรก็ตามต้องทำตามลำดับเป๊ะๆ เช่นการวนลูปมักจะเริ่มทำงานจาก i=0 ถึง n ตามลำดับเสมอ การคำนวณค่าอาจไม่ได้ทำงานตามลำดับเช่นหากต้องการจะบวกเลขในลิสต์ จะเริ่มจากตัวแรกสุด / ตัวหลังสุด / ตัวตรงกลางก่อนก็มีค่าเท่ากัน

ในบทต่อไป เราจะมาทบทวนเรื่องของฟังก์ชันในภาษาโปรแกรมกัน โดยเริ่มแรกจะเน้นที่ฟังก์ชันในภาษาสไตล์ Imperative ที่เราคุ้นชินกันก่อน แล้วค่อยแนะนำฟังก์ชันในมุมมองของภาษา FP กันต่อไปนะ

231 Total Views 3 Views Today
Ta

Ta

สิ่งมีชีวิตตัวอ้วนๆ กลมๆ เคลื่อนที่ไปไหนโดยการกลิ้ง .. ถนัดการดำรงชีวิตโดยไม่โดนแสงแดด
ปัจจุบันเป็น Senior Software Engineer อยู่ที่ Centrillion Technology
งานอดิเรกคือ เขียนโปรแกรม อ่านหนังสือ เขียนบทความ วาดรูป และ เล่นแบดมินตัน

You may also like...

ใส่ความเห็น

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องที่ต้องการถูกทำเครื่องหมาย *