🎞️ Videos → Monads in JavaScript
Description
Are you curious about Monads but find them confusing? This session is designed for beginners looking to grasp the concept in simple, relatable terms. I'll break down what Monads are using simple language and relatable analogies, such as managing boxes or following a recipe. You’ll learn the key components and discover how Monads help streamline coding, manage side effects, and improve readability and why they matter in programming without the technical jargon.
Chapters
- แนะนำตัวและหัวข้อ Monad ใน TypeScript 0:00
- ทำไมการเรียนรู้ Monad ถึงยาก 0:48
- ปัญหาของการเพิ่ม Log ใน Function อย่างง่าย 1:42
- Refactor Code เพื่อเพิ่ม Log และปัญหาที่ตามมา 2:48
- การแก้ปัญหาเบื้องต้นและการ Refactor เพื่อแยก Logic 5:02
- ออกแบบฟังก์ชัน runWithLog เพื่อจัดการ Log 7:42
- เชื่อมโยงสิ่งที่ทำกับ Monad 10:07
- Monad คืออะไร? (แบบไม่ลงรายละเอียดมาก) 11:25
- Monad คือกล่องและองค์ประกอบของ Monad 13:20
- AuditedNumber ทำงานคล้าย Monad อย่างไร 15:01
- AuditedNumber ช่วยจัดการ Log อย่างไร 18:27
- Maybe Monad: Monad ง่ายๆ ที่คุณอาจเคยใช้ 19:13
- การ Implement และใช้งาน Maybe Monad 22:04
- ประโยชน์ของ Maybe Monad ในการจัดการ Null/Undefined 24:39
- ตัวอย่างการใช้ Maybe Monad ในสถานการณ์จริง 25:46
- แก้ปัญหา Missing Value ด้วย Maybe Monad 27:59
- Monad อื่นๆ และการ Abstract งานที่แตกต่างกัน 30:22
- แหล่งข้อมูลเพิ่มเติมสำหรับการศึกษา Monad 32:21
- ช่วงถาม-ตอบ: แนะนำแหล่งเรียนรู้ Functional Programming 33:03
- ข้อดีของ Functional Programming และการประยุกต์ใช้ 34:28
Transcript
คำบรรยายต่อไปนี้อาจไม่ถูกต้องทั้งหมด หากคุณพบข้อผิดพลาดใดๆ คุณสามารถช่วยแก้ไขข้อผิดพลาดได้บน GitHub
แนะนำตัวและหัวข้อ Monad ใน TypeScript0:00
โอเคครับ สวัสดีทุกท่านอีกรอบนึงนะครับ โอเควันนี้ผมเป็นตัวแทนจาก ODDS เนาะ มาพูดใน session ของ ODDS หัวข้อที่จะพูดในวันนี้ก็คือ Monad in JavaScript นะครับ แต่จริงๆ แล้วผมลองดูแล้ว JavaScript เนี่ย ถ้าจะอธิบาย Monad มันยากเนาะ ก็เลยขอเปลี่ยนเป็น TypeScript แทนเนาะ ทุกคนก็น่าจะคุ้นเคยกันอยู่แล้ว TypeScript เพื่อให้มันเห็น type เห็นอะไรได้มากขึ้นนะครับ
โอเค ก็วันนี้ผมก็จะเล่า Monad ง่ายๆ เนาะ
ให้ทุกคนที่ยังไม่รู้จักเนี่ย ได้เห็นภาพได้มากขึ้นนะครับ
โอเคแนะนำตัวนะครับ ผมชื่อเจนะครับ ทำงานเป็น technical coach อยู่ที่ ODDS นะครับ ถ้าใครอยากจะ connect ก็ add Facebook นี้ได้เลยนะครับ
ทำไมการเรียนรู้ Monad ถึงยาก0:48
โอเคมาเริ่มกันเลยเนาะ สำหรับคนที่เคยจะศึกษา functional programming อ่ะ พอถึงจุดๆ นึงอ่ะ เราก็คงจะเริ่มรู้จัก Monad และต้องไปหาว่า Monad คืออะไร ทำไมทุกคนพูดถึงใช่ป่ะ พอเราไปศึกษาเนี่ย สิ่งแรกที่เรา search เจอก็คือประโยคนี้ใช่ป่ะ ก็คือ "A monad is just a monoid in the category of endofunctors" มันไม่ได้บอกอะไรเราเลยเนาะ เพราะว่าเรา search ว่า Monad คืออะไรเนี่ย มันลากเพื่อนมาอีก 3-4 ตัวเนี่ย Monoid, category, endofunctor ซึ่งถ้าคนที่ไม่ได้ศึกษาทางทฤษฎีทางคณิตศาสตร์อ่ะ
ก็จะไม่เข้าใจหรอกมันคืออะไร หรือว่าแม้ว่าเราจะไป search ต่อเนี่ย เราก็จะเจออะไรหน้าตาแบบนี้เนาะ มาเป็นแบบ notation ทางคณิตเต็มเลย ซึ่งถ้าเป็น developer เราก็คงไม่ค่อยมีความรู้ทางนี้มากใช่ป่ะ
ปัญหาของการเพิ่ม Log ใน Function อย่างง่าย1:42
โอเค อ่ะ ก็จะสังเกตเห็นได้ว่า
ถ้าเราเป็น developer เนี่ย การที่เราจะศึกษา functional programming โดยที่จะไม่มีแบบใครมา guide อ่ะ มันก็จะยากเนาะ มีกำแพงหนาๆ อยู่ตัวนึง เพราะฉะนั้นเดี๋ยววันนี้ เราจะมาดูกันว่ามันเป็นยังไงเนาะ ให้เราวางทฤษฎีเอาไว้ก่อน เดี๋ยวมาดูอะไรง่ายๆ ที่ทุกคนน่าจะทำกันเป็นเนาะ อ่ะ ก็คือเขียนฟังก์ชันแบบง่ายๆ สำหรับคำนวณ operation ทางตัวเลขเนาะ ในนี้ผมก็จะยกตัวอย่าง 2 ตัว ก็คือมี square กับ addOne ใช่ป่ะ square ก็คือยกกำลัง addOne คือรับ input อะไรไปก็ไปบวก 1
โดยที่ operation ทั้ง 2 ตัวเนี้ย ก็จะรับ input เป็น number แล้วก็ output ก็เป็น number เหมือนกัน อันนี้ก็ง่ายๆ ทุกคนน่าจะเข้าใจกันได้เนาะ แล้วพอเราได้ฟังก์ชันประมาณนี้แล้วเนี่ย เราก็เอามันไปใช้งานแบบนี้ใช่ป่ะ เราอยากจะใช้ square เราก็โยนตัวเลขเข้าไป แล้วก็ได้อะไรออกมา เราสามารถเอา output เนี่ย ไป chain กับ operation หรือฟังก์ชันอื่นๆ ได้เนาะ อันนี้ก็ไม่มีปัญหาอะไร ก็ 5 5 25 + 1 ก็ได้ 26 ตรงไปตรงมานะ
Refactor Code เพื่อเพิ่ม Log และปัญหาที่ตามมา2:48
แต่ถ้าในอนาคตเนี่ย เราต้องการจะ log ว่า output ของเราที่เกิดขึ้นเนี่ย มันผ่าน operation อะไรมาบ้างเนี่ย เราก็ต้องมีการแก้ code ตั้งต้นตัวนี้ของเราใช่ป่ะ เราก็ต้องแก้ว่า อ๋อแทนที่จะให้ square มัน return output ของการคูณเลขอย่างเดียวเนี่ย มันต้องทำอะไรเพิ่มนะครับ มันก็ต้อง add log เพิ่มเข้าไป แล้วก็ต้องแก้ return type เนาะ จาก number เนี่ยก็ต้องเป็น type หน้าตาประมาณนี้ โอเคใช่ป่ะ อันนี้ก็ทุกคนก็น่าจะทำกันอยู่แล้วเนาะ ก็คือ refactor code เราให้มันทำงานกับ business ใหม่ ที่เราต้องการ อันนี้ผมก็จะลอง refactor ดูเนาะ
ผมก็จะทำการ refactor code ให้มันมี log ของ operation ที่เราทำงาน
ผลลัพธ์ที่ได้ก็คือ อันที่ 1 ผมก็ต้องสร้าง type ตัวนึงใช่ป่ะ ที่บอกว่าผลลัพธ์เนี่ยมันจะประกอบไปด้วย property อะไรบ้าง ในนี้ก็ประกอบไปด้วย value ที่จะเก็บผลลัพธ์ของ operation ทางคณิตศาสตร์ แล้วก็มี log อีกตัวนึงใช้เก็บ log message ที่เป็น audit log ของเราเนาะ ตัวนี้ผมตั้งชื่อว่า AuditedNumber ใช่ป่ะ แล้วเสร็จแล้วผมก็แก้ฟังก์ชัน square ให้นอกเหนือจากการคูณเลขเฉยๆ ก็มีการแปะ log message มันออกมา อันนี้ก็ตรงไปตรงมาเนาะ ก็ทำอย่างนี้กับ addOne เหมือนกัน ถูกมั้ยครับ โอเค พอเราได้อย่างนี้แล้วเนี่ย จริงๆ code มันไม่ค่อย make sense เท่าไหร่เนาะ แต่อันนี้เพื่อการอธิบายเนาะ ปัญหาที่เราเจอตรงเนี้ยก็คือ square เนี่ย 1 คือ
ตอนเนี้ยพอเราแก้ result ของฟังก์ชันเนี่ย จะเห็นว่า input กับ output มันไม่เหมือนกันแล้วใช่ไหม input เป็น number output เป็น type นึง ผลที่ได้ก็คือเราเจอว่า เราไม่สามารถ chain ฟังก์ชันตัวเนี้ยเหมือนเดิมได้แล้วนะ เพราะว่า output มันเป็น audited number เราไม่สามารถเอา square เนี่ยไป chain ตัวเองได้ อันนี้คือปัญหาที่ 1 ที่เราเจอนะ ปัญหาที่ 2 ก็คือ addOne เนี่ย พอเราเปลี่ยน type จาก number เป็น audited number เพื่อรับผลของ square แล้วเนี่ย มันไม่สามารถเรียกใช้ได้ด้วยค่าที่เป็น type number ปกติแล้ว โอเคนี่เป็นปัญหาที่เวลาเราแก้โค้ดเราก็จะเจอกันนะ
การแก้ปัญหาเบื้องต้นและการ Refactor เพื่อแยก Logic5:02
เราก็ไม่ยากนะ อันนี้เราก็แก้ปัญหาได้ปกติทั่วไป ปัญหาที่ 1 ก็คือ ไอ้ตัว addOne เนี่ย อันเนี้ยง่าย ก็คือถ้าเราอยากจะเรียกฟังก์ชัน addOne ด้วยเลข 5 เลข 5 ปกติแล้วเนี่ย เราก็ทำไงนะ เราทำฟังก์ชันใหม่ขึ้นมาตัวนึงนะ
ที่สามารถ transform number ให้กลายเป็น audited number ของเราได้ใช่ไหม เราก็สามารถแก้ปัญหาว่า addOne ถ้าเราอยากจะเรียกตัวเดียวโดดๆ เนี่ยทำไง เราก็ใช้ฟังก์ชันตัวเนี้ย 𝚠𝚛𝚊𝚙𝚆𝚒𝚝𝚑𝙻𝚘𝚐 ตัวเนี้ย เป็นการสร้าง type audited number ขึ้นมาจากเลข 5 ปกติ โอเคอันนี้ก็จะแก้ปัญหาที่ 1 ได้นะ อีกปัญหานึง square เราเนี่ย ที่ input กับ output มันไม่เหมือนกันเนี่ย แล้วมันไม่สามารถ chain ตัวเองได้ เราก็แก้ให้มันเหมือนกันเอง อันนี้ก็แก้ปัญหาง่ายๆ นะ เราก็จากที่เรามี 𝚠𝚛𝚊𝚙𝚆𝚒𝚝𝚑𝙻𝚘𝚐 เนี่ย เราก็สามารถสร้าง audited number เพื่อให้ใช้งานใน square ได้ เราก็แก้ input type ของ square ให้กลายเป็น audited number โอเคอันนี้เราก็พอเราแก้ปัญหาได้แล้วเนี่ย ปัญหาตอนแรกที่เราเจอว่า เฮ้ยเราทำแบบนี้ไม่ได้ เราก็สามารถเรียกการใช้งานฟังก์ชัน
ด้วยการช่วยเหลือของฟังก์ชัน utility ตัวนี้นะ ทำให้เราสามารถมี audit log ได้แล้วว่า เราเกิดการทำงานอะไรขึ้นบ้างใน operation พวกนี้ โอเค ซึ่งจริงๆ แล้วเนี่ยตรงเนี้ย มันก็โค้ดมันก็น่าจะพอขึ้น production ได้แล้วนะ เพราะว่ามันก็ทำงานได้ตาม business ที่เราต้องการ แต่จริงๆ แล้วในมุมของ software engineering เนี่ย นอกจากที่มันจะทำงานได้นะ มันก็ควรจะ make sense ด้วย โค้ดเราควรจะ make sense ด้วย เราก็เลยลองมาดูอีกว่าสิ่งที่เราทำไปเนี่ย มันเกิดอะไรขึ้นบ้างนะ โค้ด square กับ addOne เนี่ย สิ่งที่มันเกิดขึ้นคือ
มันทำงานคล้ายๆ กันอยู่ใช่ไหม เรายังไม่ต้องดูว่ามันทำงานอะไรเลย แค่มองโค้ดเนี่ย เราก็จะเห็นว่าหน้าตาของโค้ดเนี่ย มันแทบจะเหมือนกันเลยใช่ไหม แสดงว่ามันอาจจะมีอะไรบางอย่าง duplicate กันอยู่ โอเคนะ ซึ่งพอมัน duplicate กันเนี่ย ปกติเราก็ต้องจำแนกก่อนนะว่าอะไรดูบ้าง ในตัวอย่างเนี่ยผมจะจำแนกออกมาเป็น 3 ส่วนนะ ส่วนที่ 1 ก็คือการคำนวณเลข ทั้ง 2 operation เนี่ยมีการคำนวณเลขที่เหมือนกันเลย ส่วนที่ 2 ก็คือ มันจะทำการ concat log ของตัวเอง
เข้าไปที่ input เพื่อที่จะ return ออกไป
โอเคนะ เพราะงั้นไอ้พวกเนี้ยก็คือการทำงานซ้ำกัน แต่จริงๆ แล้วเรารู้กันอยู่แล้วนะว่า ฟังก์ชันที่ดีเนี่ยมันควรจะทำงานแค่อย่างใดอย่างหนึ่งใช่ไหม เพราะงั้นเราควรจะ extract โค้ดบางส่วน ที่มันไม่เกี่ยวกับฟังก์ชันนั้นออกไป โอเคนะ เราก็มาลอง refactor โค้ดกันมา
ออกแบบฟังก์ชัน runWithLog เพื่อจัดการ Log7:42
ยกตัวอย่าง square นะ square addOne ตะกี้มันหน้าตาเหมือนๆ กันนะ ผมก็จะยกตัวอย่างแค่ square square เนี่ย ก่อนจะเริ่มทำการ refactor เราก็ควรจะระบุให้ได้ก่อนนะว่า จริงๆ แล้วโค้ดไหนเนี่ย เป็น logic ของ square โค้ดไหนเนี่ยไม่ใช่ของมัน ผมจะลอง refactor ให้ดู ผมแยกโค้ดออกมาและชุดที่ 1 ตรงเนี้ย ที่ผมบอกว่ามันคือ logic ของ square ก็คือ เป็นการยกกำลังเลขใช่ไหม แล้วก็มี log ของ square อันเนี้ยมันเห็นได้ชัดแล้วว่ามันเป็นโค้ดของ square จริงๆ อีกอันนึงก็คือโค้ดสำหรับ concat log ของตัวเอง ว่าทำงานอะไรออกไป ก่อนที่จะ return โค้ดออกไปนะ เคมี 2 จุดนี้นะ ซึ่งอันเนี้ย เราเห็นได้แล้วว่าไอ้เนี้ย มันไม่ make sense นะที่จะอยู่ใน square เท่าไหร่เลย เพราะว่ามันเป็นการ concat log ใช่ไหม เราก็เลยอยากจะพยายามเอามันออกไป
เมื่อเรารู้แล้วว่าเราอยากจะ extract ตัวนี้ออกไป เราก็ design ฟังก์ชันนะว่า เราจะเอาฟังก์ชันแบบไหนมาลองรับการทำงานแบบนี้ อันเนี้ยผมก็อยากจะตั้งชื่อมันว่า 𝚛𝚞𝚗𝚆𝚒𝚝𝚑𝙻𝚘𝚐 ตัวเนี้ยผมบอกว่า ผมอยากให้เป็นฟังก์ชัน ที่รองรับการ concat log โดยที่ฟังก์ชันเนี้ย รับ input 2 ตัวนะ ตัวที่ 1 ก็คือ 𝚠𝚛𝚊𝚙𝚆𝚒𝚝𝚑𝙻𝚘𝚐 ที่รับตัวเลขอะไรบางอย่างเข้ามา เพื่อมาเป็น input parameter ตัวที่ 1 อีกอันนึงคือฟังก์ชันที่จะถูก apply ด้วย parameter ตัวที่ 1 ตัวนี้ เพราะฉะนั้นถ้าผมเรียกโค้ดหน้าตาแบบเนี้ย ผลที่ได้ก็คือมันจะเอา audited number ที่มีค่า 5 ใช่ไหม
โยนเข้าไปในฟังก์ชัน square ได้อะไรออกมา ก่อนจะ return ให้ใส่ log ให้ด้วย โอเคไหม เพราะงั้นตอนเนี้ยโค้ดจะมีหน้าตา มีการทำงานเป็นของมันเองและ พอเรา design เสร็จแล้วนะ เราก็แยกโค้ดออกมา อันเนี้ย ตรงไปตรงมาใช่ไหมของ square ง่ายสุด แยกออกมาเลยเป็นฟังก์ชัน square เคไหม แล้วก็เราก็สร้างฟังก์ชัน 𝚛𝚞𝚗𝚆𝚒𝚝𝚑𝙻𝚘𝚐 ที่รับ parameter 2 ตัวตามที่ design ไว้นะ ตัวแรกคือ audited number ตัวที่ 2 เป็นฟังก์ชัน transform แล้วก็ก่อน transform เราก็เอา logic ของการ concat log มาแปะให้มันนะ เค
เราก็สามารถเรียกด้วยฟังก์ชันแบบนี้ได้แล้วใช่ไหม ตรงไปตรงมาเนาะ
เชื่อมโยงสิ่งที่ทำกับ Monad10:07
สุดท้าย พอเราทำเสร็จแล้วเนี่ย เราก็ไปทำกับ addOne ด้วยในภาพเดียวกันเลย เราจะได้โค้ดทั้งหมดหน้าตาประมาณนี้เนาะ เราจะมี AuditedNumber ที่เป็น type ของ result ที่เราอยากได้เนาะ เรามีฟังก์ชัน wrapWithLog ที่รับพารามิเตอร์ตัวที่ 1 คือ number แล้วมันก็จะ transform ออกมาเป็น AuditedNumber โอเคไหม แล้วก็มี operation ทางคณิตศาสตร์ 2 ตัวของเราตอนแรก แล้วก็อีกอันนึงที่สำคัญคือ runWithLog ตัวนี้เป็นฟังก์ชันที่ใช้ compose ระหว่าง AuditedNumber แล้วก็ฟังก์ชันของเรา แล้วมันก็จะ concat log ออกมาให้เรา โอเคไหม
เพราะฉะนั้นตอนนี้โค้ดเรา มันมีความ มีการทำงานที่เป็นของใครของมันแล้วก็ทำงานแค่อย่างเดียวจริงๆ โอเค มาถึงตรงนี้แล้วเนี่ย โค้ดตัวนี้เราทำด้วยกันเนี่ยเสร็จ แต่หลายคนอาจจะสงสัยว่า เอ๊ะ ผมกำลังพูดอะไรอยู่ มันเกี่ยวกับ monad ยังไงใช่ไหม อยากจะบอกว่า สิ่งที่เราทำทั้งหมดเนี่ย มันดันไปคล้ายการทำงานของ monad ตัวนึงมาก โอเคไหม
ก่อนที่เราจะไปต่อเดี๋ยวให้สร้าง checkpoint ไว้ตรงนี้ก่อน ว่าเราเพิ่งทำ type AuditedNumber ที่มีการทำงานคล้าย monad
ทำ checkpoint ไว้เนาะ แล้วเดี๋ยวเราค่อยกลับมาดูมัน
Monad คืออะไร? (แบบไม่ลงรายละเอียดมาก)11:25
กลับไปตอนแรกเลยที่ผมยังไม่ได้อธิบายต่อนะว่า monad คืออะไร disclaimer ไว้ก่อน ว่าจริงๆ แล้วเนี่ย ก่อนที่เราจะศึกษาเรื่อง monad เนี่ย กว่าจะมาถึงตรงนี้ได้ถ้าเราไปอ่านหนังสือจริงๆ มันจะมีความรู้พื้นฐานอีกเต็มเลย กว่าจะมาถึงตรงนี้ได้ อย่างเช่น functional programming ใช่ไหม functor applicative category theory อะไรอย่างเงี้ย
เต็มไปหมดเลย ซึ่งใน session วันนี้มันเป็นไปไม่ได้เลยที่จะอธิบายทั้งหมดใช่ไหม session นี้ผมก็เลยพยายามจะดึงเฉพาะ character ของ monad ที่มันอาจจะมาช่วยในการทำงานเราในชีวิตประจำวันได้เนาะ โดยที่เราอาจจะต้องยังไม่ต้องเข้าใจ monad ทั้งหมดก็ได้ โอเคไหม มันทำให้ talk เนี่ยมันอาจจะมีเนื้อหาบางอย่างเนี่ยหายไป แต่ผมหวังว่าทุกคนน่าจะพอเห็นภาพว่า monad เนี่ยมันใช้งานยังไง โอเค ไปกันต่อ
เริ่มจากจริงๆ แล้ว พอได้ยินคำว่า monad เนี่ย ทุกคนก็คิดว่า เอ๊ะ มันต้องเป็นอะไรที่แบบยาก ต้องรู้คณิตศาสตร์ ต้องรู้ category theory ใช่ไหม แต่จริงๆ แล้ว สำหรับคนที่จะเพิ่งเริ่มใช้งานน่ะ ผมอยากให้ลองวางความคิดทางทฤษฎีเอาไว้ก่อน แล้วเริ่มจากคิดว่าจริงๆ แล้ว monad เนี่ย มันเป็นแค่กล่องเนาะ
ที่อาจจะมีเครื่องมือต่างๆ ที่จะมาช่วยเราในเรื่อง computation กับ value โอเคไหม โดยที่มันน่ะ สิ่งที่มันช่วยเราอ่ะ มันจะช่วยเราได้หลายอย่าง
ยกตัวอย่างเช่น จะช่วยเรื่อง asynchronous ช่วยเรื่อง error handling หรือว่าช่วยเรื่องการทำ accumulate log แบบที่เราได้โชว์เห็นใน AuditedNumber เมื่อกี้เนาะ
Monad คือกล่องและองค์ประกอบของ Monad13:20
โอเค เราลองเริ่มจินตนาการก่อน จินตนาการก่อนว่า monad เนี่ยมันเป็นกล่องนึง ที่เก็บ value อยู่ข้างในนะ
แล้ว monad ตัวนี้จะอนุญาตให้เราสามารถ apply
ฟังก์ชันใดๆ เข้าไปกับ value ข้างในนั้น โดยที่ก่อนที่มันจะ apply ฟังก์ชันเนี่ย มันจะมี context บางอย่างมาครอบ เป็นการ control ว่าฟังก์ชันกับ value จะถูก apply กันท่าไหน โอเคไหม อันนี้ก็คือจินตนาการก่อนเนาะว่า monad มันจริงๆ มีแค่นี้แหละ แล้วก็การที่จะเป็น monad เนี่ย มันจะประกอบไปด้วย 3 component ใหญ่ๆ จริงๆ มันมีมากกว่านี้นะ แต่ยกตัวอย่างแบบหลักๆ มาเนาะ 1 ก็คือ monadic type ตัวนี้เนาะ monadic type ก็คือ type ของ monad ของเรา ตรงไปตรงมา ก็คือเราก็ต้องมี type ก่อน เสร็จแล้ว อันที่ 2 ก็คือ unit operation unit operation คือ operation ที่จะรับ input เป็น plain type แล้วมันจะ return monadic value ออกมาให้เรา โอเคไหม อันนี้ก็เรียกว่า unit เนาะ บางภาษาก็จะเรียกว่า return หรือ pure แล้วแต่ว่าเราไปเจอในภาษาไหน สุดท้ายเราจะมี bind operation bind operation น่ะ มันจะเป็น operation ที่จะรับพารามิเตอร์ 2 ตัว ตัวที่ 1 ก็คือ monadic value อีกตัวนึงจะเป็นฟังก์ชันที่ใช้ transform value ตัวเมื่อกี้ แล้วตัวฟังก์ชันเนี่ยจะ return monadic value ตัวใหม่ออกมาให้เรา โอเคไหม อันนี้เรียกว่า bind operation
AuditedNumber ทำงานคล้าย Monad อย่างไร15:01
ซึ่งถ้าใครตามทันเนาะ ใครยังไม่เมาเนี่ย ก็จะเริ่มเห็นแล้วมันคล้ายกับไอ้ตัว audited number เราที่เพิ่งทำตะกี๊เนาะ หนึ่งคือ audited number เรามี type ใช่ป่ะ เรามี type ชื่อ audited number มี property ตามนี้ ต่อมาเรามีฟังก์ชัน wrapWithLog ที่รับ number แล้ว return audited number ออกไป โอเคมั้ย มันจะอิงกับคล้ายๆ นิยามตัวนี้เนาะ รับ type นึง return อีก type นึงออกไปให้ โอเคมั้ย
สุดท้ายเรามี runWithLog ที่รับ parameter สองตัว เหมือนนิยามตัวนี้เนาะ ตัวนึงเป็น audited number เป็น monadic value อีกอันนึงเป็นฟังก์ชันที่ใช้ transform monadic value นั้น แล้วก็ return type ใหม่ออกมา โอเคมั้ย
ความคล้ายกันสูงเนาะ แต่สำหรับคนที่ยังอาจจะแบบ เอ๊ย ฟังก์ชันนี้มันอยู่แยกกันนี่ ทำไมมัน เรามองเป็นไงเป็นส่วนประกอบ component มัน มันมองยากใช่ป่ะ เราลอง refactor มันให้อยู่ในรูปของ class โอเค
เมื่อเวลาเรา refactor เสร็จแล้วเนี่ย เราก็จะได้ class ที่ชื่อว่า AuditedNumber จาก interface เมื่อกี๊เนาะ กลายมาเป็น class และ AuditedNumber มี property สองตัว แล้วเราก็จะมี method ที่ชื่อว่า of ตัวนี้ ตัวนี้มันคือ unit operation ที่แปลงมาจากฟังก์ชัน wrapWithLog เมื่อกี๊ เราก็แค่เปลี่ยนชื่อมันแล้วก็แก้ type นิดหน่อย อีกอันนึงคือ bind operation ตัวนี้เป็น bind method ก็แปลงมาจาก runWithLog ที่เราทำไปตอนแรก copy มาเลยแล้วก็เปลี่ยน type นิดหน่อย แล้วเราก็ลากพวก operation ทางคณิตศาสตร์เรามาเหมือนเดิม โอเคมั้ย ซึ่งสังเกตว่าเนี่ย ตอนนี้มันมาอยู่ด้วยกัน และทำให้การใช้งานก็จะเปลี่ยนไปนิดนึงเนาะ จากตอนแรกที่เราเรียกฟังก์ชันโยนฟังก์ชัน เราก็มาเปลี่ยนไปเป็นเราสร้าง instance ของ audited number ขึ้นมา แล้วเวลาเราจะ apply ฟังก์ชันกับตัวเลขตัวนั้นเนี่ย เราก็ใช้ฟังก์ชัน bind แทน แล้วก็โยนฟังก์ชันเข้าไป แทนที่เราจะเรียกฟังก์ชันแบบ JavaScript ปกติเนาะ อย่างเงี้ย อันนี้ยกตัวอย่างเช่น ถ้าเราต้องการจะบวกเลข 5 ขึ้นมาอย่างเงี้ย เราก็ transform ก่อน เลข 5 ให้กลายเป็น audited number ที่มีค่า 5 ใช่มั้ย
เสร็จแล้วเราก็อยากจะบวก 1 เราก็ bind ฟังก์ชัน addOne ให้มันเข้าไป อยากจะเอาไป square เราได้ผลลัพธ์ แล้วก็ bind ฟังก์ชันมันเข้าไปเรื่อยๆ อันนี้ก็ไม่น่าจะยากเนาะ น่าจะตามกันทัน หรือในแนวทางของ functional programming เนี่ย บางทีเค้าไม่อยากจะมา declare ตัวแปรเยอะแยะเลย เค้าก็สามารถ chain operation ในมุมนี้ได้ใช่ป่ะ code มันก็จะ declarative มากขึ้นเนาะ โอเค มาถึงจุดนี้ปุ๊บ
เราก็จะคิดได้ว่าเนาะ สิ่งที่ audited number หรือเราพยายามทำเนี่ย เราพยายามจะดึงความรับผิดชอบอะไรบางอย่าง ออกจาก operation พวกนี้ ให้ฟังก์ชันพวกนี้มันยังทำงานแค่อย่างเดียวอยู่ สิ่งที่ audited number มันทำก็คือ มันพยายามดึงเอา logic ของการ concat log เนี่ย จากภาพแรกที่เราพยายาม refactor มาเนาะ เราให้แต่ละฟังก์ชันเนี่ยมันทำการ concat log เองใช่ป่ะ ซึ่งจริงๆ แล้วมันไม่ค่อยถูกต้อง เราก็เลยดึงความรับผิดชอบนั้นออกมาอยู่ที่ audited number แล้วเมื่อเราเอา audited number ไปใช้ bind กับฟังก์ชันคำนวณต่างๆ เนี่ย ฟังก์ชันพวกนี้ไม่ต้องมีความรู้เกี่ยวกับการ concat log เลย เราเขียนให้มันเป็น pure function ปกติ แล้วความรับผิดชอบในการ concat log ก็จะมาอยู่ที่ audited number แทนเนาะ
AuditedNumber ช่วยจัดการ Log อย่างไร18:27
เมื่อเรามอง type เนี่ยเรา ถ้าเราใช้กันเยอะๆ อ่ะ เราก็จะรู้แล้วว่า อ๋อ audited number เนี่ยมันจะต้องมี log อยู่ ถูกป่ะ
เวลาเอาไปใช้เราก็ เวลาเราไป chain operation เนี่ย เราไม่ต้องคิดว่า เอ๊ย เราจะ log ที่ไหน เรารู้ว่าตัวนี้จะเป็นคนจัดการให้ โอเคนะ
หรือว่าถ้าเราจะกล่าวง่ายๆ เนาะว่า ถ้าเราให้ audited number เนี่ย เรา implement ตามนิยามของ monad ทุกข้อแล้วเนี่ย เราจะหมายถึง audited number เนี่ย มันจะมี context อะไรบางอย่างที่จะมาช่วยเรา context นั้นก็คือการจัดการเรื่อง log ของ operation ที่ทำงานอยู่บน number โอเคมั้ย เรามี operation ที่ทำงานอยู่บน number เนี่ย เราไม่ต้องทำการ concat log เลย เพราะว่ามันจะทำอัตโนมัติอยู่ใน type audited number ตัวนี้เนาะ
Maybe Monad: Monad ง่ายๆ ที่คุณอาจเคยใช้19:13
โอเค สำหรับคนที่ยังงงๆ อยู่เนาะ เดี๋ยวมาดู monad ที่ง่ายที่สุด แล้วก็ทุกคนอาจจะเคยใช้นะ สำหรับคนที่เขียนหลายภาษาอย่างเงี้ย อาจจะเคยใช้ monad ตัวนี้ โดยที่ยังไม่รู้ว่าตัวนี้มันคือ monad เลยด้วยซ้ำ สิ่งนี้เราเรียกว่า maybe monad โอเคมั้ย maybe monad เนี่ยเป็น monad ตัวแรกๆ ที่เราจะได้เจอถ้าเราได้ศึกษาเรื่อง monad ซึ่งในภาษาอื่นเนี่ยเราจะเรียกมันว่า option optional หรือ nullable ใช่มั้ย
optional เนี่ยน่า ผมเห็นหลายภาษามากใช้คำว่า optional โอเค ซึ่ง maybe เนี่ย ความหมายของมันก็คือ ยกตัวอย่างเช่น ถ้าเรา declare type ของตัวแปร เป็น number ความหมายที่เราจะนิยามให้ตัวแปรนั้นน่ะ มันก็คือตัวแปรนั้นเป็น number ใช่ป่ะ แต่ถ้าเราบอกว่าตัวแปรนั้นน่ะ มี type เป็น maybe number ความหมายของมันก็คือจะเป็น number หรืออาจจะไม่มีอะไรอยู่ก็เป็นได้ โอเคมั้ย ก็คืออาจจะเป็น null หรือ undefined ใช่ป่ะ ซึ่งถ้ามองในมุมของ TypeScript มันก็คือ union type ใช่ป่ะ เราทำ union type อีกอันนึงก็คือสมมติว่ามันเป็น maybe user ก็อาจจะหมายถึงว่า user หรือไม่มีอะไรก็ได้ โอเคมั้ย
ซึ่งจากนิยามเมื่อกี๊ เราก็จะเห็นว่าตัว maybe เนี่ย มัน represent ถึง state สองตัว ก็คือ just หมายความว่ามันมีค่าอะไรบางอย่างอยู่ หรือบางภาษาจะเรียกว่า some ก็เป็นอันเดียวกันเนาะ ก็คือ maybe นั้นมีค่าอะไรบางอย่างอยู่ข้างใน อีกอันนึงคือ nothing maybe นั้นไม่มีอะไรอยู่ข้างในเลย เก็บค่า null โอเคมั้ย
ซึ่งการทำงานของแต่ละ state เนี่ย มันก็จะทำงานไม่เหมือนกันเนาะ ยกตัวอย่างเช่น ผมต้องการเอา maybe 𝑚 เนี่ย ไป bind กับฟังก์ชัน 𝑔 มันหมายถึงผมต้องการจะเอาค่าที่อยู่ใน 𝑚 เนี่ย ไป apply ในฟังก์ชัน 𝑔 เนาะ ก็คือ 𝑔 แล้วก็ค่าอะไรบางอย่างใช่ป่ะ อ่ะ เริ่มจากเรามี maybe 𝑚 อยู่ ถ้าเราต้องการจะ apply ฟังก์ชัน 𝑔 เข้าไปเนี่ย จะ bind 𝑔 เนี่ย สิ่งที่ monad มันจะทำก่อนก็คือ มันไปเช็กก่อนว่าใน monad maybe monad อะน่ะ มีค่ามั้ย ถ้ามันมีค่าเป็น 𝑥 มันจะ return monad ตัวใหม่ออกมา
ที่เป็น maybe monad นะ แล้วข้างในนั้นน่ะ ก็จะเก็บ value ของ 𝑔𝑥 ไว้ โอเคมั้ย ก็คือมีการ apply ฟังก์ชัน 𝑔 เข้าไป แต่ถ้า maybe ไม่มีค่าเลย เป็น nothing สิ่งที่มันทำก็คือ return กล่องเปล่ามาให้ ก็คือ maybe nothing โอเคมั้ย อันนี้ง่ายๆ เนาะ
การ Implement และใช้งาน Maybe Monad22:04
อ่ะ มาลองดู implementation เนาะ จริงๆ แล้ว เราไม่ต้องเขียนเองนะ เพราะว่ามันมี library ให้เราเลือกใช้เยอะมาก เค้าเขียน monad มาให้เราเนี่ยเต็มไปหมดเลย เราไปเลือกหยิบมาใช้ได้ แต่เนี่ย เพื่อที่จะให้เห็นภาพก็ลองมาดูว่า จริงๆ แล้ว maybe มันทำงานยังไงเนาะ ซึ่งใน code ตัวเนี้ยก็จะประกอบไปด้วย code 2 กลุ่มหลักๆ ก็คือ กลุ่มที่เป็น unit operation ใช่มั้ย monad มี unit operation ใช่มั้ย ซึ่งเป็นฟังก์ชันที่ใช้ transform plain type ให้กลายเป็น monadic type
อีกกลุ่มนึงก็คือเป็น map กับ bind นะ เป็น bind operation เนาะ ก็คือเป็นการ apply ฟังก์ชันเข้าไปใน monad ตัวนั้น ซึ่ง map กับ bind เนี่ย ทำงานคล้ายกันเลย ต่างกันนิดเดียว map เนี่ย มันจะรับ input เป็นฟังก์ชันที่ return type 𝑢 นะ แล้วมันก็จะ ตัว map เองเนี่ย มันจะ return maybe 𝑢 ออกมาให้ โอเคมั้ย ฟังก์ชัน return 𝑢 ของออกจาก map จะเป็น maybe 𝑢 ส่วน bind เนี่ย ฟังก์ชัน จะ return maybe 𝑢 ใช่มั้ย แต่ bind อ่ะ จะ flat maybe ออกให้ตัวนึง แล้วก็ return maybe 𝑢 เหมือนเดิม โอเคมั้ย ต่างกันแค่เนี้ย แต่มันมี use case ใช้การที่ต่างกันเนี่ยแหละ เออ ก็เลยต้องมี 2 อัน
โอเค มาลองดูการใช้งานมันเนาะ สมมุติว่าเราต้องการจะใช้ maybe บวกเลข โอเคมั้ย ใช้ maybe บวกเลข เราก็ต้อง transform type ที่เป็น number เนี่ย ให้กลายเป็น maybe number ก่อน โดยการใช้ unit operation เนาะ ในที่นี้เราก็ใช้ from มันก็จะเริ่มจาก Maybe.from(10) เนี่ย เอ่อ code ชุดเนี้ย ผลของการ run ก็จะได้ maybe แล้วก็มีค่า 10 อยู่ข้างในใช่ป่ะ เค แล้วถ้าเราต้องการจะบวกเลข เช่น จะบวก 1 เนี่ย เราก็ใช้ map แล้วก็โยนฟังก์ชันบวก 1 เข้าไปให้มัน โอเคมั้ย เมื่อเรา map code ตรงนี้แล้วเนี่ย ผลที่ได้จาก code บรรทัดเนี้ยก็คือ maybe 11 ใช่มั้ย เราก็ map ได้เรื่อยๆ เนาะ จะ map กี่ทีก็ 𝑥 + 2 ก็เป็น maybe 13 𝑥 + 3 ก็เป็น maybe 16 ใช่ป่ะ เพราะฉะนั้นผลของการ run code ชุดข้างบนเนี่ย ก็จะได้ maybe 16 เนาะ นี่ก็แบบ basic ทั่วไป
อีกเคสนึงก็คือ ถ้าฟังก์ชันระหว่างทาง
ไม่รู้มันทำอะไรอ่ะ แต่สุดท้ายมัน return ค่า null ออกมาแบบเนี้ย ผลของการ map บรรทัดเนี้ย มันจะ return maybe nothing ออกมา แล้วเมื่อเราเอา maybe nothing เนี่ย ไป map ต่อ มันก็จะไม่ทำอะไรเนาะ มันก็จะ return nothing ออกมาให้เราเลย เพราะฉะนั้นมันก็จะได้ maybe nothing เนาะ มีค่า null อยู่ข้างใน โอเคมั้ย
ประโยชน์ของ Maybe Monad ในการจัดการ Null/Undefined24:39
จากตัวอย่าง 2 ตัวเนี้ย เราก็จะเห็นแล้วว่า มันมีประโยชน์เนาะ maybe มันมาช่วยเราในการทำให้เราสามารถ compose ฟังก์ชัน
หรือ map ฟังก์ชันหลายๆ ตัวได้ โดยที่เราไม่ต้องกังวลว่าฟังก์ชันระหว่างกลาง อันใดอันนึงเนี่ยมันจะ return null ออกมา แล้วทำให้มันได้ null pointer ใช่ป่ะ ถ้าเราไม่ได้ใช้ maybe แล้วเอาตัวเลขปกติไปบวกกัน แล้วไม่รู้ว่าสุดท้ายมีฟังก์ชันอะไรฟังก์ชันนึง มา return nothing ออกมาหรือ null เนี่ย แล้วเราไป map ต่อมันก็อาจจะเกิด error of undefined throw exception null pointer บลาๆ โอเคมั้ย อันนี้ก็เป็นประโยชน์ของ maybe เนาะ แล้วก็ตะกี้ที่ติดเรื่อง map กับ bind ใช่มั้ย map เนี่ย สมมุติว่า ถ้าเราไป apply maybe
กับฟังก์ชันที่มันดัน return maybe อีกตัวนึงออกมา ผลของการ run เนี่ยมันจะได้ maybe ซ้อน maybe ซึ่งไม่ใช่โครงสร้างที่เราอยากได้เนาะ เราอยากได้ maybe ที่เก็บ value อะไรใช่ป่ะ อันนี้วิธีแก้ปัญหาก็คือ ถ้าเราเจอฟังก์ชันแบบนี้เราก็ไปใช้ bind แทน มันก็จะ flat maybe ออกให้ตัวนึง อ่านี่ก็เป็นวิธีการใช้งาน ก็ขึ้นอยู่กับว่าเราจะไป apply กับฟังก์ชันหน้าตาแบบไหน
ตัวอย่างการใช้ Maybe Monad ในสถานการณ์จริง25:46
โอเค พอเรารู้จัก maybe ละ เราลองมาดูตัวอย่างที่อาจจะเจอในชีวิตประจำวันได้ง่ายมากขึ้นเนาะ
เช่น ถ้าผมต้องการยกตัวอย่างว่า ผมต้องการจะสร้างฟังก์ชัน ฟังก์ชันนึงโดยที่ฟังก์ชันเนี่ย เป็น pseudocode เนาะ เราเวลาเราออกแบบฟังก์ชันต้องคิดก่อนว่า ฟังก์ชันนั้นทำงานอะไรบ้างใช่ป่ะ ฟังก์ชันเนี่ยผมบอกว่ามันคือฟังก์ชันชื่อว่า 𝚐𝚎𝚝𝙿𝚎𝚝𝙽𝚒𝚌𝚔𝚗𝚊𝚖𝚎 ก็คือจะ get ชื่อสัตว์เลี้ยงออกมาจากคนๆ นึง แล้วก็จะ return ชื่อออกมาก็เป็น string ใช่ป่ะ โดยที่การทำงานเนี่ยผมอยากให้มีขั้นตอนดังนี้เนาะ 1 ก็คือ จะ getCurrentUser ออกมา get คนออกมาว่าคนๆ นั้นน่ะตอนเนี้ยเป็นใคร แล้วก็ 2 ก็คือจะ get สัตว์เลี้ยงออกมาจากเขาเนาะ get pet จาก user ที่เราได้มาเมื่อกี้ อ่า แล้วเสร็จแล้วพอเราได้สัตว์เลี้ยงปุ๊บ เราก็ไปเอาชื่อสัตว์เลี้ยงออกมาใช่ป่ะ อันนี้ก็เป็นเหมือนกับเป็น business logic ของฟังก์ชันนี้เนาะ ซึ่งชีวิตจริงเนี่ยเราไม่น่าได้เจออะไรง่ายๆ อย่างงี้ใช่ป่ะ เพราะว่าการ getCurrentUser เนี่ยสมมตินะ ตัวเนี้ย getCurrentUser มันอาจจะเจอหรือไม่เจอ user ก็ได้ใช่ป่ะ เช่นถ้า user ยังไม่ได้ล็อกอินก็ไม่มี user อ่า เพราะฉะนั้นสิ่งพวกนั้นน่ะเราเรียกว่า side effect ที่เกิดขึ้นในโค้ดที่เราไม่สามารถ predict ได้ว่า result มันจะออกมาเป็นอะไรกันแน่
เพราะฉะนั้นจริงๆ แล้วโค้ดในชีวิตจริงอะ ถ้าเราเอา pseudocode เมื่อกี้ไป implement อะ เราจะได้โค้ดหน้าตาแบบนี้ใช่ป่ะ ก็คือ getCurrentUser เนี่ย มันก็อาจจะเจอ user หรือ undefined ก็ได้แล้วแต่ แล้วปกติก่อนจะเอา user ไปใช้ต่อเนี่ย เราก็ต้องมีการ handle undefined หรือ handle null พวกนี้เนาะ เป็นกับทุกอันเลย ถ้า getUser มาแล้วเป็น undefined ก็ไม่ต้องทำก็ข้ามไป แต่ถ้าไม่เป็น null หรือ undefined มีค่าอยู่ เราก็เอาไป get pet ต่อ ภาพเดียวกันเลยถ้า user คนนั้นไม่มี pet เราจะทำไง เราก็ต้อง handle ต่อไปเรื่อยๆ ใช่ป่ะ ทำให้ตอนเนี้ยโค้ดเราอะ มันมีสิ่งที่ไม่เกี่ยวกับ business logic จริงๆ เข้ามาเกี่ยวข้อง
ซึ่งสมมติว่า จริงๆ ถ้าคน handle ครบ ก็อาจจะมองไม่ มันไม่ใช่ปัญหาอะไรหรอก แต่สมมติว่าเราไม่รู้ละว่า สมมติว่าเราเขียน JavaScript อะ เราไม่รู้ type ว่ามันคืออะไรอะ เราก็สามารถอาจจะลืม handle undefined หรือ null พวกนี้เนาะ ทำให้โปรแกรมมันระเบิดข้างหน้าบน production ได้
แก้ปัญหา Missing Value ด้วย Maybe Monad27:59
โอเคมั้ย อ่า พอเรามีปัญหาแบบนี้เนี่ย ด้วยความรู้ที่เรามีเมื่อ 5 นาทีก่อนเนี่ย เรารู้จัก maybe เนาะว่า maybe อะ มันสามารถเอามาช่วยเรื่องการ handle undefined หรือ no value ได้ อ่า วิธีการแก้เราคือแทนที่
ฟังก์ชันแต่ละตัวของเราตรงเนี้ย จะ return type ออกมาตรงๆ เราเอามันไปครอบด้วย maybe ก่อน เมื่อเรา implement เสร็จแล้วมันจะออกมาเป็นหน้าตาแบบนี้ ก็คือ getCurrentUser เนี่ย เราก็จะได้แทนที่เราจะได้ user หรือ undefined แบบนี้เนาะ เราจะได้ maybe user แทน อ่า แล้วเราก็เอา user เนี่ยไป bind กับฟังก์ชันอื่นๆ ของเรา นะก็คือการ apply ฟังก์ชันด้วย ด้วยตัวแปรตัวนี้ใช่ป่ะ แล้วอ่า ถ้าขั้นตอนใดขั้นตอนนึงเนี่ย มันไม่เจอของมัน return null หรือไม่มีอะไรเงี้ย มันก็จะไม่ impact กับฟังก์ชันข้างหลังเลยใช่ป่ะ มันทำให้เราสามารถเอาการ handle โค้ดพวกเนี้ยออกไปได้เนาะ
หรือในมุมของ functional เนี่ย เราก็จะ compose ฟังก์ชันออกมาเป็นหน้าตาแบบนี้ได้เลย เพราะว่าเราไม่ต้อง handle พวกนี้แล้วอะ มันจะ declare ตัวแปรเยอะแยะทำไมใช่ป่ะ ก็ไม่ต้องเราก็เอาฟังก์ชันมา compose กัน ทำให้ฟังก์ชันเราอะมันอ่านง่ายมากขึ้นเนาะ โอเค อะ อะ ตอนเนี้ยนอกจากที่ว่าเรารู้แล้วว่า maybe อะ
มันช่วยให้เราทำการจัดการ missing value หรือ null value ได้ดีขึ้นแล้วเนี่ย เพราะว่ามันจัดการเองเลยด้วย type ของตัวแปรใช่ป่ะ ผลที่ได้ตามมาแบบอัตโนมัติเลยก็คือโค้ดเราอะ อ่านได้ง่ายมากขึ้นเนาะ เพราะว่าจะมีแค่โค้ดที่เป็น business logic จริงๆ ไม่ได้มี case มีโค้ดสำหรับจัดการ null จัดการ side effect จัดการนู่นจัดการนี่ที่ไม่เกี่ยวเลย เวลาเราเห็นโค้ดรกๆ อะ ส่วนใหญ่ครึ่งนึงอะ มันจะเป็นการจัดการอะไรพวกนั้นน่ะ ไม่เกี่ยวกับ business เลยเนาะ เออ มันก็ทำให้โค้ดเราอ่านง่ายขึ้นแบบอัตโนมัตินะ
สำหรับคนที่ยังงงอยู่อีกว่ามันคืออะไรกันแน่เนี่ย จริงๆ ก็อยากให้คิดง่ายๆ เนาะ ถ้าคนยังงงหรือว่าไม่เข้าใจเนี่ย ก็คิดง่ายๆ ว่า มันเป็น monad เนี่ย มันอาจจะเป็นเหมือนกับ design pattern ตัวนึงเนาะ ที่จะทำให้เราสามารถ chain operation หรือ chain function ได้ โดยที่เราไม่ต้องจัดการเรื่อง side effect หรือจัดการงานอะไรนอกจาก business logic ตัว monad มันเองเนี่ย มันจะเป็นคนจัดการให้เราเอง โอเคมั้ย
Monad อื่นๆ และการ Abstract งานที่แตกต่างกัน30:22
จริงๆ monad เนี่ย
ที่เราว่ามันจัดการเองอ่ะ คือในการบอกง่ายๆ ก็คือมันสามารถ abstract งานบางอย่างออกไปให้เรา โดยที่เราไม่ต้อง aware เรื่องนั้นเองใช่ป่ะ ซึ่งแต่ละ monad แต่ละตัวเนี่ย มันก็จะทำการ abstract งานต่างๆ แตกต่างกันออกไป ขึ้นอยู่กับว่าเราใช้ monad ตัวไหนนะ ยกตัวอย่างเช่น Writer Monad ก็จัดการเรื่องการทำ log accumulation ซึ่งตอนที่เราเขียนไปตอนแรกอ่ะ คือ AuditedNumber เนี่ย ถ้าเราไปดู implementation ของ AuditedNumber กับ Writer Monad ในภาษาอื่นๆ ที่เป็น functional programming อ่ะ มันจะทำงานคล้ายๆ กันเลย โอเคนะ ต่อมาคือ Maybe เนี่ย ก็จะจัดการเรื่อง missing value ให้เรา เราไม่ต้องจัดการเอง เดี๋ยว Maybe จัดการให้ ต่อมาคือ Either จัดการเรื่อง error handling ถ้าเราเขียนแบบ imperative ทั่วไปเนี่ย เราจะ throw error catch error นู่นนี่นั่นใช่ป่ะ แต่ถ้าในโลกของ functional programming เราใช้ monad เนี่ย เราใช้ Either มาจัดการเรื่อง error handling พวกนี้ ตัวสุดท้ายที่ยกตัวอย่างคือ Future ตัวนี้จะจัดการเรื่อง asynchronous เนาะ ซึ่ง Future เนี่ยจริงๆ ใน JavaScript เรามีสิ่งที่คล้ายกับ Future อยู่คือ promise ใช่ป่ะ เราก็ไม่ต้องจัดการเรื่อง asynchronous เองเนาะ แต่จริงๆ promise ใน JavaScript ก็เขียนไม่ค่อยถูก 100% ตามนิยามเนาะ แต่ก็อยู่ในกรุ๊ปเดียวกันนี่แหละ
โอเค สำหรับคนที่ดูแล้วยังไม่เข้าใจอีก
โยนความคิดเลย โยนทิ้งไปเลย ไม่ต้องสนใจว่า monad คืออะไร เราให้คิดง่ายๆ ว่า คนๆ เนี้ย คุณ Bartosz เนี่ย เขาเคยบอกว่าถ้าไม่เข้าใจเนี่ย ให้คิดง่ายๆ เลยว่า monad เนี่ย มันก็เป็นแค่วิธีการ compose function แค่แบบนึงอ่ะ โดยที่ function ที่เราเอามา compose อ่ะ มันจะมี type ที่พิเศษกว่าชาวบ้านเค้านิดนึงเนาะ เราสามารถ compose function ต่างๆ โดยที่ทำให้ function นั้นน่ะ มันจัดการเรื่อง side effect เองได้
แหล่งข้อมูลเพิ่มเติมสำหรับการศึกษา Monad32:21
สำหรับคนที่สนใจอยากจะศึกษาต่อเนาะ
ก็ลองไปหาหนังสือเล่มนี้ Category Theory for Programmers เนาะ เขียนโดยคุณอาจารย์คนนี้แหละ เขาก็จะพยายามอธิบาย category theory ให้ developer เข้าใจได้ง่าย
มีคณิตศาสตร์แหละ แต่มีน้อย
คิดว่ามันน่าจะอ่านง่ายกว่า category theory สำหรับที่นักศึกษาทางคณิตศาสตร์เค้าศึกษากันนะครับ โอเค สำหรับคนที่อยากจะเข้าใจต่อก็ลองไปหาเล่มนี้มาอ่านดูเนาะ โอเค อันนี้สำหรับ talk วันนี้ผมก็มีเท่านี้ครับ ขอบคุณที่รับฟังครับ ขอบคุณครับ
ช่วงถาม-ตอบ: แนะนำแหล่งเรียนรู้ Functional Programming33:03
ฮัลโหล ขอบคุณครับพี่ โอ้โห มีแฟนคลับเยอะมากเลย ก็สำหรับ section monad นี้เรียกว่าตัวผมเองก็เผลอเขียนมันไป แบบไม่รู้ตัวเหมือนกันครับ แต่ไม่กล้าพูด วันนี้งาน JavaScript พอดีเขียน Go โอเคครับ มีใครมีคำถามมั้ยครับ
มีคำถาม 1 ท่านครับ เดี๋ยวซักครู่นะครับ อย่าเพิ่งหนี อย่าเพิ่งไปนะครับพี่ เอิ่ม พอจะมีคำแนะนำมั้ยครับว่า ถ้าจะศึกษา functional programming เพิ่ม มีที่ไหนที่ฝึกศึกษาแล้วแบบ efficient ที่สุด จริงๆ แล้วถ้าอยากเขียน functional programming จริงๆ เลยนะ อยากให้ลองเขียนด้วยภาษา Haskell มันจะบังคับเราทุกอย่างให้เขียนเป็นแบบ functional เลย เราจะไม่มีทางหลุดจากการเป็น functional way แต่ถ้าเราศึกษาด้วยภาษาอื่น อย่างเช่น JavaScript เนี่ย มันเขียนออกทะเลอะไรยังไงก็ได้ เราคุมคอนโทรลไม่ได้เลย แต่ถ้าเป็น Haskell เนี่ย เราจะได้ศึกษาทั้ง type มันเป็นยังไง monad คืออะไร เราจะสามารถใช้ monad ได้ โดยที่เราไม่รู้เลยว่ามันคือ monad เพราะว่ามันมี type บังคับเราอยู่ครับ แล้วมี use case ที่ใช้งานกับแอปพลิเคชันจริงๆ
ที่มันช่วยให้งานเราง่ายขึ้นนะครับ
ข้อดีของ Functional Programming และการประยุกต์ใช้34:28
จริงๆ แล้วความง่ายขึ้นของการใช้ functional programming ก็คือ หลักๆ ของ functional programming เนี่ย core ที่หลายคนเค้าเคยศึกษา เค้าจะบอกว่ามันจะ avoid เรื่องการ mutate state หรือการสร้าง side effect ใช่มั้ย สมมติเรามี function ยาวๆ จริงๆ business logic เรายาวมากเลย แล้วเราเขียนแบบ mutate state มั่วไปมาเนี่ย อนาคตที่เรามาแก้โค้ดเนี่ย มันจะเริ่มแบบ สะกิดตรงนี้ระเบิดตรงนั้น สะกิดตรงนั้นระเบิดตรงนี้ใช่ป่ะ แต่ถ้าเราเขียนโค้ดให้มันเป็นไปในแนวทางของ functional programming ครับ มันจะมีโอกาสเกิดปัญหาจากการที่เราไปเจอ side effect พวกนั้นน่ะได้น้อยลงครับ
ต้องขอบอกว่าจริงๆ แล้วการเขียนเนี่ย ไม่ใช่ทุกคนเขียนได้หรือเขียนเป็นครับ
ทีมก็ต้องศึกษา จริงๆ มันต้องมีการ knowledge sharing เนาะว่า
จริงๆ แล้วการเขียนโค้ดเนี่ย มันเขียนได้หลายแบบ เขียนแบบ A เป็นไง เขียนแบบ B เป็นไง OOP ก็ดี ไม่ใช่ไม่ดีนะครับ ส่วน functional ก็ดี ขึ้นอยู่กับว่าเราสามารถ apply งั้นมาในงานเราได้ยังไงบ้างเนาะ จริงๆ แล้ว ไม่รู้ว่าทุกคนเขียน JavaScript ในชีวิตประจำวันรึเปล่านะ เราเขียน JavaScript เนี่ย มันเป็นภาษาที่สามารถ allow ให้เราเขียนได้หลายท่ามาก เราก็สามารถ mix ได้เนาะว่า ตรงเนี้ยมันควรเป็น OOP นะ ส่วนตรงเนี้ย function มันเป็น pure function ได้ ทำเป็นแบบ functional ได้ ก็ไม่ผิดที่จะใช้ 2 อย่างรวมกันนะครับ
ครับ ขอบคุณครับ