*บทความนี้เป็นบทความที่เขียนมาตั้งแต่บล๊อกเก่า (ปี2015) แต่เพิ่งย้ายมาลงที่นี่
รวมเนื้อหาเกี่ยวกับการเขียนโปรแกรมแนว Functional และหัวข้ออื่นๆ ที่เกี่ยวข้อง
บทความชุด: Functional Programming
- ตอนที่ 1 Pure, First-Class, High-Order, พื้นฐานแห่ง Functional Programming
- ตอนที่ 2 Lambda Function and Closure ฟังก์ชันทันใจและพื้นที่ปิดล้อม!
- ตอนที่ 3 map filter reduce และเพื่อนๆ พระเอกแห่งโลก Functional
- ตอนที่ 4 โครสร้างแบบ Pair และ Either กับ List Comprehension การสร้างลิสต์ฉบับฟังก์ชันนอล
- ตอนที่ 5 Lazy Evaluation ขี้เกียจไว้ก่อนแล้วดีเอง?!
- ตอนที่ 6 Recursive Function ฟังก์ชันเวียนเกิด, เขียนลูปไม่ได้ทำยังไง? มาเขียนฟังก์ชันเรียกตัวเองแทนกันเถอะ!
- ตอนที่ 7 Curry, Partial Function
- ตอนที่ 8 Functor, Monad
- [บทความปี 2015] มารู้จักกับ Functional Programming สิ่งที่คุณต้องรู้ในตอนนี้กันเถอะ
- Function ทำงานยังไง?, ในมุมมองของโปรแกรมเมอร์สาย Imperative
Category Theory & Lambda Calculus
— OOP cannot save us from the Cloud Monster anymore.
"การเขียนโปรแกรมแบบ OOP(เชิงวัตถุ)ไม่อาจช่วยคุณได้อีกแล้ว!!"
บล๊อกนี้เป็นการแปลอีกแล้ว ช่วงนี้มีแต่แปลบทความทั้งที่เรื่องที่อยากเขียนก็มีเยอะนะ
เอาล่ะ บล๊อกนี้ออกจะคล้ายๆ บล๊อกที่แล้ว ภาษาและเฟรมเวิร์คไหนบ้างที่ Developer ควรจะศึกษาประจำปี 2015 นั่นคืออะไรที่เราควรจะสนใจในปี 2015 นั่นแหละนะ
สำหรับวันนี้เราจะมาพูดถึง...
กันละนะ (แปลจาก Functional Programming should be your #1 priority for 2015 ) และเหมือนเดิมคือเราไม่ได้แปลตรงๆ แต่เรียบเรียงใหม่ด้วยนะ เพราะงั้นเนื้อหาอาจจะไม่เป๊ะกับต้นฉบับล่ะ
โอเค เริ่ม!
ถ้าคุณเป็นโปรแกรมเมอร์ที่ทำงานเขียนโปรแกรมอยู่ทุกวันนี้คุณน่าจะเคยได้ยินว่า "Clojure / Scala / Erlang หรือแม้แต่ Java ก็มี
Lambdas แล้วนะ!" แปลว่ามันต้องมีอะไรที่เกี่ยวข้องกับ Functional Programming อย่างแน่นอน
แล้วมันคืออะไรน่ะ?
ว่าแต่ เจ้า Functional Programming นี่มันคืออะไร?
รูปแบบการเขียนโปรแกรมแบบใหม่ต่อจาก OOP อย่างงั้นเหรอยังไง?
ไม่! --- ไม่เลย ถ้าลอง Google คำว่า Functional Programming
คุณจะเห็นว่าไม่มีอะไรใหม่เลย เจ้าภาษาที่ใช้รูปแบบ Functional Programming บางตัวเช่นภาษา Lisp เองก็มีอายุมากกว่า 60 ปีแล้ว
คำถามคือแล้วเราจะมาสนใจอะไรกับรูปแบบการเขียนโปรแกรมที่ใช้มาตั้งแต่ยุคปี 1950 โน่นล่ะ
ตอนที่คอมพิวเตอร์เกิดขึ้นมาบนโลกใบนี้มันช้าเอามากๆ ดังนั้นไม่ใช่ว่าใครจะทำอะไรก็ได้ (หมายถึงเขียนโปรแกรมแบบไม่แคร์ความเร็วน่ะนะ) เขาจึงมีแนวคิด 2 อย่างคือ
- เริ่มจากสถาปัตยกรรมคอมพิวเตอร์แบบ Von Neumann แล้วใส่ Abstraction เข้าไป
- หรือ...เริ่มจากคณิตศาสตร์ และนำ Abstraction ต่างๆ ออกไป
abstractions เยอะๆ ซึ่งเจ้า functional เนี่ยมันต้องใช้เยอะเลยล่ะดังนั้นปัญหาของ
Lisp มันทำตัวไฮโซเกินกว่ายุคสมัย คอมสมัยนั้นรันตามมันไม่ค่อยทัน (ทันแหละ แต่ช้า~) ยิ่งต่อมายุคของ Imperative Programming (เขียนโปรแกรมแบบบอกเป็นขั้นตอนว่าจำทำอะไรบ้าง เช่นภาษา C) ได้มาถึงและจัดการเทคโอเวอร์โลกโปรแกรมมิ่งไปเรียบร้อย โดยเฉพาะภาษา C ที่ฮิตติดลมมาก
มาถึงยุคใหม่ ยุคที่คอมพิวเตอร์ทำงานได้เร็วมาก โปรแกรมเมอร์เขียนโปรแกรมกันโดยไม่ต้องแคร์อะไรทั้งนั้น (ส่วนใหญ่นะจ๊ะ) โปรแกรมส่วนใหญ่สามารถที่จะทำงานได้โดยไม่ต้องสนเลยว่าเขียนด้วยภาษาอะไรมาและนั่นเองคือ...โอกาสที่สองของ Functional Programming
Functional Programming 50.5
ปกติชื่อวิชาเวลาเราเรียนมันจะเป็น Functional Programming 101แต่บล๊อกนี้ไม่ใช่บทความที่จะมาสอนคุณเกี่ยวกับ Functional Programming แต่จะเกริ่นๆ ให้พอที่คุณจะเริ่มปิ๊งไอเดียเกี่ยวกับ Functional Programming พอที่จะเอาไปลุยต่อเองละกัน
Functional Programming คือการเขียนโปรแกรมด้วย Functionเอ๊ะ เอ่อ...ยังไม่เห็นภาพสินะ เอาใหม่ๆการเขียนสไตล์นี้จะให้คุณสามารถสร้างฟังก์ชันหลายๆ ตัวขึ้นมาประกอบกันได้ ในบทความต้นฉบับเขายกตัวอย่างฟังก์ชั่น f ∙ g ตอนเรียน Calculus ในวิชาคณิตศาสตร์ แต่เราเชื่อว่าคนส่วนใหญ่มักไม่ถูกกันมันเท่าไหร่ (เราก็ไม่ถูกกับคณิศาสตร์มากนัก) ก็ขอข้ามๆ ไปละกัน
มาดูเรื่องเด่นๆ ใน Functional Programming กันดีกว่า
1. Pure Functions
มันคือฟังก์ชันรูปแบบที่ง่ายที่สุด ชนิดที่เรียกได้ว่าใครเขียนโปรแกรมเป็นต้องรู้จักมันแน่นอน ... สรุปคือเจ้านี่คือฟังก์ชันธรรมดานี่แหละ ท่าปกติ ไม่ใช่ท่ายากแต่อย่างใด
function add(a, b){ return a + b; }
ฟังก์ชั่นแบบปกติ ถ้าคุณเรียก add(1, 2) แล้วมันให้คำตอบ 3 กลับมา ไม่ว่าจะเรียก add(1, 2) ซะกี่ครั้งมันก็จะให้ผลออกมาเหมือนเดิม เป็นฟังก์ชั่นจริงๆ แท้ๆ ตอนคอนเซ็ปเลย
2. First-Class Functions
หมายถึงการที่เราสามารถจับฟังก์ชันยัดลงไปเป็นตัวแปรได้ (ปกติเวลาเราสร้างตัวแปรก็มีแค่ int double String Object อะไรประมาณนั้นใช่มั้ยล่ะ) ตัวอย่างนี้ถ้าคุณเขียน JavaScript (ที่เลยระดับbasicแล้วนะ) คุณน่าจะเจอบ่อย
var add = function(a, b){ return a + b; }
คงเห็นแล้วล่ะสิ ว่าเราประกาศตัวแปรขึ้นมา แล้วบอกให้ตัวแปรนี้มีค่าเป็นฟังก์ชันตัวหนึ่งเลย (เรียกฟังก์ชั่นไร้นามแบบนี้ว่า Anonymous Function)
ดังนั้นตัวแปร add จะทำตัวกลายเป็นฟังก์ชั่นเลย จะเรียก add(1, 2) ก็ได้เลย
3. High-Order Function
var add = function(a){ return function(b){ return a + b; } }
ส่วนเวลาใช้ก็...
var add2 = add(2); var ans = add2(3);
ฟังก์ชั่นตัวแรกไม่ได้ return ค่ากลับมาเป็นคำตอบเลย แต่ให้ฟังก์ชั่น(อีกตัวหนึ่ง)กลับมา ต้องเอาตัวนี้ไปใช้อีกทีก่อน ถึงจะได้คำตอบล่ะนะแต่ลองสังเกตว่า ฟังก์ชั่นทั้งสองตัวต่างก็รับ parameter (คือฟังก์ชั่นตัวนอกรับ a มา ส่วนฟังก์ชั่นตัวในก็รับ b มาอ่ะนะ) แต่ที่ฟังก์ชั่นด้านในสามารถหยิบตัวแปร a จากข้างนอกไปใช้ได้ด้วยโดยที่อะไรก็ตามที่อยู่ข้างนอกฟังก์ชั่นตัวนอก ไม่สามารถจะหยิบ a ไปใช้ได้คอนเซ็ปนี้เราเรียกมันว่า
4. Closures
ตอนที่อธิบายไปเมื่อกี้ละนะกรณีมีฟังก์ชันซ้อนๆ กันเยอะๆ ฟังก์ชั่นตัวในสามารถเรียกตัวแปรข้างนอกมันมาใช้ได้ทั้งหมดเลย แต่ด้านนอกใช้ของข้างในไม่ได้นะ
5. Immutable State
ข้อสุดท้ายนี่เราไม่ค่อยเห็นว่ามันจะเป็นประโยชน์เท่าไหร่ แต่มันก็เป็นคุณสมบัติอย่างหนึ่งของ Functional Programming ละนะ นั่นคือการรักษา state เอาไว้แม้จะมีการเซ็ตค่าตัวใหม่ลงไปก็ตาม เช่น
let x = 5; x = 6; print_int x;
ตัวอย่างนี้เป็นภาษา OCaml ตัวแปร x ตอนแรกเราประกาศ let เอาไว้หมายความว่าไม่อยากให้มันเปลี่ยนค่าแล้วนะ บรรทัดต่อไปที่กำหนด x = 6 เลยไม่เป็นผล หลังจากเอาค่า x มาลองprintดูมันก็ยังจะได้ 5 อยู่เหมือนเดิม
Object-oriented Programming cannot save us anymore
จากการใช้งานคอมพิวเตอร์ให้คิดหรือประมวลผลให้เราบนคอมเครื่องเดียว ตอนนี้โลกเทคโนโลยีเดินทางมาถึงยุคที่ไม่ว่าจะทำอะไรก็ต้องผ่าน Internet แบบงานออนไลน์รายได้ดี (ไม่ใช่ //ผิดๆ)ทุกอย่างกำลังจะขึ้นไปล่องลอยอยู่บนก้อนเมฆด้วยระบบคอมแบบ Parallel Distribute System หรือ Cloud (หัวข้อ Cloud ยังไม่เคยเอามาเขียนลงบล๊อก แต่เรื่อง Parallel Computing เคยเขียนไปนิดหน่อย ถ้าอยากอ่านก็ไป ที่นี่ได้เลย!)ในคอนเซ็ปคือกระจายคอมถิวเตอร์ออกไป ให้พวกมันช่วยกันคิดๆ ไม่ว่าอยู่ที่ไหนก็ทำได้ แต่ด้วยคอนเซ็ปนี้แหละที่นำความยุ่งยากมาสู่ชีวิตโปรแกรมเมอร์อย่างเราๆ ท่านๆ
แล้วพอเป็น Cloud ทำไมถึงสร้างปัญหาให้กับ OOPเหตุผลคือ...
ไม่ต้องถึงระดับ OOP แต่แค่ Imperative Programming ต่างก็มีรู้แบบการทำงานที่ค่อนข้างละเอียด ต้องมีการเปลี่ยนสถานะที่เกี่ยวข้องกับ self ไม่ก็ this (คอนเซ็ป OOP เลยล่ะ คือมองโปรแกรมมิ่งให้เป็นของชิ้นๆ แบบในโลกจริงไงล่ะ)
แต่เพราะมันดีเกินไป คือโครงสร้างหนักมาแต่ไกล (แต่ถือว่าใช้งานได้ดีมากนะ) มันเยอะเกินไปสำหรับกรณีที่ความต้องการคือ ขออะไรง่ายๆ ไม่ซับซ้อนและเชื่อถือได้เพราะ Distribute System และ Cloud มันมีคอมหลายเครื่องในระบบของมัน แต่ละเครื่องไม่น่าจะทำงานพร้อมๆ กันหรือแม้แต่เป็นเครื่องรุ่นเดียวกัน ถ้าเอา OOP ไปใช้มันจะเป็นอะไรที่คุมยากมากเลยใช่ไหม --- วิธีการของ Functional Programming ที่เน้นไปที่ฟังก์ชั่น ใส่ input เข้าไปแล้วก็จะได้ output ออกมา แค่นั้นแหละ! จบ! พอแล้ว...เวลาเอาไปใช้บนเครื่องในระบบที่แตกต่างกันเราก็มั่นใจได้ว่าถ้าสั่งแต่ละเครื่องทำงานไปสัก 1,000 ครั้งมันก็ยังจะให้ค่าคำตอบกลับมาเหมือนเดิมโดยไม่ต้องไปสั่งสร้าง object อะไรให้วุ่นวาย
แต่ก็ใช่ว่าภาษาพวก OOP จะตายแล้วนะ มันยังอยู่ แต่ส่วนใหญ่ก็เพิ่มคุณสมบัติของ Functional เข้าไปให้ใช้ด้วยแล้วในรูปของ Lambda Function ยังไงล่ะ เช่นพวก Java, C++11, Ruby, Python หรือแม้แต่ภาษาน้องใหม่แต่ไม่ด้อยความสามารถจากค่าย Apple อย่าง Swift
งั้นฉันจะเริ่มยังไงดี?
ตอนแรกที่จำทำความเข้าใจ Functional Programming อาจจะตะกุกตะกักซะหน่อย มันก็เหมือนกับตอนที่เพิ่งเปลี่ยนจาก เขียนโปรแกรมธรรมดามาใช้ OOP นั่นแหละ
ตอนเริ่มแรกอยากจะให้คุณลืมคอนเซ็ปการเขียนโปรแกรมแบบเดิมๆ ไปก่อนแล้วจะเริ่มใหม่ได้ง่ายกว่า หลายๆ อย่างใน Functional
Programming อาจจะไม่ค่อยเป็นภาษาคน อ่านค่อนข้างยาก เห็นแล้วเดาไม่ออกว่าบรรทัดนี้เอาไว้ทำอะไร
ยิ่งถ้าคุณเริ่มเขียนโปรแกรมด้วยภาษาแนว Imperative เช่น C มาด้วยนะ Functional อาจจะกลายเป็นตัวประหลาดไปเลย
แต่เกริ่นมานี่่อย่าเพิ่งไปกลัวมัน คุณแค่ยังไม่ชินกับแนวทางการเขียนของมันเท่านั้น
เอาล่ะ มาดูตัวอย่างกันเถอะ
guess :: Int -> [Char] guess 7 = "This is a Se7en" guess x = "Oops, no~"
อ่านไม่รู้เรื่องละสิ ไม่เป็นไร เราก็อ่านไม่รู้เรื่องเหมือนกัน (ฮา) อันนี้เป็นภาษา Haskell งั้นเราลองมาดูโค้ดนี้ในภาษา JavaScript ที่เราน่าจะพอคุ้นๆ กันบ้างดีกว่า
function guess(x){ if(x == 7){ return "This is a Se7en"; } else{ return "Oops, no~"; } }
หวังว่าคงรู้เรื่องกันนะว่าตกลงมันแปลว่าอะไร (55)
เอ้า อธิบายซะหน่อย ... ตอนแรกบอกว่า guess เป็นฟังก์ชันที่จะรับ Int เข้ามาแล้วตอบกลับเป็น String ( [Char] หรือ Array of Char ไงละ)
จากนั้นบอกต่ออีกว่าถ้า Int ที่ว่าเป็น 7 ให้ตอบ "This is a Se7en" นะ แต่นอกจากกรณีนั้นให้ตอบว่า "Oops, no~"
ไม่ชินละสิ มาดูอีกตัวอย่างๆ
plus1 :: [Int] -> [Int] plus1 [] = [] plus1 (x:xs) = x + 1 : plus1 xs
ตัวอย่างนี้เราจะสร้าง plus1 ซึ่งเป็นฟังก์ชั่นที่เอาไว้ "บวก1" ให้เลขทุกตัวใน Array เช่นถ้ารับ [1, 10, 54] ไปก็จะตอบกลับเป็น [2, 11, 55] มาให้
เริ่มจากการบอกว่าฟังก์ชันนี้รับ Array of Int มาแล้วจะตอบ Array of Int กลับไปให้นะๆ
จากนั้นบอกว่าถ้า Array ที่รับมามันว่าง ไม่มีเลขไรเลยอ่ะ ก็ตอบ Array ว่างแบบนั้นกลับเลย (ไม่มีประโยชน์ที่จะทำไง)
หลังจากนั้น (งงนิดหน่อยนะ) คือเราจะเรียกเลขตัวแรกว่า x ส่วนที่เหลือจะเรียกว่า xs ... ให้เอา x ไปบวก 1 จากนั้นตัวที่เหลือที่เป็น xs น่ะ ให้เอาไปทำงานแบบ plus1 ต่อ (คอนเซ็ปคล้ายๆ เวลาเราเขียน Recursive Function ใน C)
function plus1(arr){ if(arr.length == 0){ return []; } for(var i = 0; i < arr.length; i++){ arr[i]++; } return arr; }
ถ้าเขียนแบบ Imperative ธรรมดาก็จะได้ประมาณนี้แหละ คนละสไตล์กัน ... แต่ที่อยากทำให้ดูคือ ภาษาพวก Imperativeตอนนี้ก็เขียนแบบ Functional ได้แล้วแบบนี้
var arr = [1, 10, 54]; arr.map(function(x){ return x + 1; });
หมายความว่า เรามี arr อยู่ แต่อยากเปลี่ยนค่าทุกตัวใหม่ (ด้วยวิธีเดียวกันทุกตัว) เราเลยสั่ง mapping มันซะ แล้วโยนฟังก์ชั่นตัวหนึ่งแบบ First-Class Function ไปให้มัน บอกว่าสำหรับ x (เป็นตัวแทน data ตัวหนึ่งๆ ใน arr) ให้เอาทุกตัวมาทำแบบ x + 1 ซะ
So, let's get started
สำหรับ Functional Programming คุณไม่ควรพลาดแหล่งข้อมูลดีๆ ตามนี้เลย
- Principles of Functional Programming in Scala
- Introduction of Functional Programming (Contents)
- Paradigms of Computer Programming --- Fundamentals
หรือสำหรับคนช่วยสัมผัสกระดาษจริง (หมายถึงหนังสือพวก Textbook น่ะนะ) ก็ไปหาเล่มพวกนี้
- Structure and Interpretation of Computer Programs
- How to Design Programs
- Concepts, Techniques and Models of Computer Programming
บทความชุด: Functional Programming
รวมเนื้อหาเกี่ยวกับการเขียนโปรแกรมแนว Functional และหัวข้ออื่นๆ ที่เกี่ยวข้อง
- ตอนที่ 1 Pure, First-Class, High-Order, พื้นฐานแห่ง Functional Programming
- ตอนที่ 2 Lambda Function and Closure ฟังก์ชันทันใจและพื้นที่ปิดล้อม!
- ตอนที่ 3 map filter reduce และเพื่อนๆ พระเอกแห่งโลก Functional
- ตอนที่ 4 โครสร้างแบบ Pair และ Either กับ List Comprehension การสร้างลิสต์ฉบับฟังก์ชันนอล
- ตอนที่ 5 Lazy Evaluation ขี้เกียจไว้ก่อนแล้วดีเอง?!
- ตอนที่ 6 Recursive Function ฟังก์ชันเวียนเกิด, เขียนลูปไม่ได้ทำยังไง? มาเขียนฟังก์ชันเรียกตัวเองแทนกันเถอะ!
- ตอนที่ 7 Curry, Partial Function
- ตอนที่ 8 Functor, Monad
- [บทความปี 2015] มารู้จักกับ Functional Programming สิ่งที่คุณต้องรู้ในตอนนี้กันเถอะ
- Function ทำงานยังไง?, ในมุมมองของโปรแกรมเมอร์สาย Imperative
1 Response
[…] เคยเขียนบทความเรื่อง Functional Programming ไปแล้วครั้งนึง สามารถอ่านแบบละเอียดได้ที่: http://www.tamemo.com/post/80/functional-programming-should-be-your/ […]