Mo Lang เมื่อผมอยากได้ความรื่นรมย์ในการเขียนโปรแกรม

Share
Mo Lang เมื่อผมอยากได้ความรื่นรมย์ในการเขียนโปรแกรม

เมื่อก่อนผมเขียน Go เยอะมาก มันเป็นภาษาที่ดีและเรียบง่าย เริ่มง่าย ศึกษาง่าย ผมมักพูดเสมอว่า Go มันเหมือนมีดทำกับข้าวของจีนที่เรียบง่ายและทรงพลัง อย่างไรก็ตามหลังจากเขียนไปนานก็พบว่า

ทำไมมันน่าเบื่อจัง ยิ่งเขียนยิ่งรู้สึกว่ามันเป็นภาษาที่เหี่ยวแห้ง

ตัดสินใจหยุดเขียน Go

ผมพบว่าการเขียน Go มันทำให้ความรื่นรมย์ในการเขียนโค้ดหายไปเพราะว่าเราไม่สามารถเขียนโค้ดที่อธิบายความคิดของเราได้

package vanilla

import (
	"errors"
	"fmt"
	"strings"
)

type User struct {
	ID   int
	Name string
}

func FindUser(id int) (User, error) {
	db := map[int]User{1: {ID: 1, Name: "Alice"}}

	user, ok := db[id]
	if !ok {
		return User{}, errors.New("user not found")
	}
	return user, nil
}

func ValidateName(u User) (User, error) {
	if len(u.Name) < 3 {
		return User{}, errors.New("name too short")
	}
	return u, nil
}

func FormatOutput(u User) string {
	return fmt.Sprintf("USER_%d: %s", u.ID, strings.ToUpper(u.Name))
}

func ExampleCorrected() {
	user, err := FindUser(1)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	user, err = ValidateName(user)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	fmt.Println(FormatOutput(user))
}

เราจะเห็นว่าเนื้อหาของ ExampleCorrected มันดูเต็มไปด้วยของที่ผมไม่อยากอ่าน ผมก็รู้นะว่า ภาษามันถูกออกแบบมาให้ทำของบางอย่างให้ explicit และมันจะดีมากเลยถ้า!!!!! เราสามารถเลือกได้ว่าเราจะ explicit ตอนไหนและจะ implicit ตอนไหน ความน่าเบื่อของ if err != nil มันทำให้ผมถอยห่างจากภาษานี้ไปที่ละนิด ทีละนิด เพราะยิ่งเขียนไปก็ยิ่ง เหนือยกับสิ่งนี้และด้วยความเคารพใน principles ของภาษานี้ท้ายที่สุดผมก็หยุดเขียน Go ไปเลยเพราะไม่อยากบิดวัฒนธรรมอันดีงาม

กลับมาเขียน Scala

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

def processFeed(
    fetch: String => Option[String],
    parse: String => Option[List[FeedItem]],
    formatter: List[(String, String)] => String
)(url: String): Option[String] =
  for
    fetched <- fetch(url)
    parsed <- parse(fetched)
    formatted = formatter(parsed.map(item => (item.title, item.link)))
  yield formatted

นี่น่าจะเป็นสิ่งที่ผมตามค้นหา หลังจากนั้นก็ได้กลับไปเข้าใจ concept ต่างๆที่อยู่เบื้องหลังการออกแบบภาษานี้จนได้เข้าใจว่า ของที่ Scala มีนั้นจริงๆแล้วเราสามารถเอาไปเขียนได้ใน ภาษาไหนก็ได้เพราะมันเป็น pattern ที่อยู่ใน functional programming อย่างไรก็ตามเราจะทำออกมาได้แค่ไหนนั้นมันขึ้นอยู่กับพื้นฐานของภาษานั้นๆ ผมก็เลยได้พัฒนาคลาสใหม่ที่ชื่อว่า Refactoring to Functional Patterns ขึ้นมาเพื่อ อธิบายวิธีคิดที่เป็นขั้นเป็นตอนในการทำให้น้องๆเข้าในแนวคิดการแก้ปัญหาแบบ Functional และเมื่อทำไปสักพักก็เริ่มคิดว่า

ทำไมเราไม่สอนด้วย Go Lang แต่ว่า Go Lang เขามีมาตรฐานการเขียน ที่ดีอยู่แล้วถ้าเราจะไป เขียนแบบแหวกม่านประเพณีเขาน่าจะดูไม่งาม เราเรียกมันว่าเป้น Mo Lang เลยละกัน

กลับมาเขียน Mo Lang

ทำไมต้อง Mo Lang จริงๆไม่ได้มีอะไรซับซ้อน หลักๆเพราะว่าผมไปใช้ lib ที่มีน้องท่านหนึ่งมา comment ใน Facebook ว่าพี่ๆๆๆๆ ลองใช้ samber/mo (https://github.com/samber/mo )สิมันดีมากเลยนะผมก็เลยลองไปใช้ดูสิ่งที่พบคือ จากโค้ดที่ดูเหี่ยวแห้งในตัวอย่างด้านบนเราสามารถแปลงร่างมันให้เป็นแบบนี้ได้

package composition

import (
	"fmt"
	"strings"

	"github.com/samber/mo"
	"github.com/samber/mo/option"
)

type User struct {
	ID   int
	Name string
}

// Module 1: Option - Replacing nil pointers
func FindUser(id int) mo.Option[User] {
	db := map[int]User{1: {ID: 1, Name: "Alice"}}

	user, ok := db[id]
	if !ok {
		return mo.None[User]()
	}
	return mo.Some(user)
}

// Module 2: Result - Railway Oriented Programming
func ValidateName(u User) mo.Option[User] {
	if len(u.Name) < 3 {
		return mo.None[User]()
	}
	return mo.Some(u)
}

func FormatOutput(u User) string {
	return fmt.Sprintf("USER_%d: %s", u.ID, strings.ToUpper(u.Name))
}

func ExampleCorrected() mo.Option[string] {
	// The "Pure" Pipeline style: Option[int] -> Option[User] (lookup) -> Option[User] (validate) -> Option[string]
	output := option.Pipe3(
		mo.Some(1),
		option.FlatMap(FindUser),
		option.FlatMap(ValidateName),
		option.Map(FormatOutput),
	)
	return output
}

ผมรู้สึกได้เลยว่า นี่มันรื่นรมย์มาก สิ่งนี้ทำให้ผมรู้สึกสนุกกับการเขียน Go อีกครั้ง ไม่สิไม่ใช้ Go มันคือ Mo Lang ผมได้ความรู้สึกของ ความสามารถในการอธิบายความตั้งใจ ของผมเข้าไปในโค้ดได้อย่างตรงอย่างที่ผมคิดในหัว

นี่แหละ Mo Lang ของผมภาษาที่ compile เร็วเหมือน Go และเขียนสนุกเหมือน Scala

Read more

The Five Ideals

The Five Ideals

จากหนังสือ The Unicorn Project ในนั้นจะมีที่ปรึกษาเก่ง ๆ คนหนึ่งชื่อ Erik เป็นเหมือนเซ็นเซหาตัวยาก เดี๋ยวก็มา เดี๋ยวก็ไป ถ้าโชคดีก็จะเจอเค้าว่าง แล้วเค้าก็จะสอนอะไรที่สำคัญ ๆ แต่ฟังดูล้ำ ๆ และลึกซึ้ง

By Chokchai Phatharamalai
Communication, Reliability and Modern concepts - System Design, Simply Explained

Communication, Reliability and Modern concepts - System Design, Simply Explained

บทความนี้จะอธิบายพื้นฐานของ System Design โดยต่อยอดจากตัวอย่าง Gym Application เพื่อให้เห็นภาพว่า เมื่อระบบเริ่มเติบโตและถูกแยกออกเป็นหลาย Service แต่ละ Service จะสื่อสารและทำงานร่วมกันอย่างไร งานแบบไหนควรสื่อสารแบบ Synchronous งานแบบไหนควรประมวลผลแบบ Asynchronous รวมถึงอธิบาย Concept พื้นฐานต่าง ๆ ที่ควรรู้ เช่น API

By Boonsong Srithong
Race condition

Race condition

ผมกำลังอ่านหนังสือ The Unicorn Project ซึ่งเป็นนิยายเกี่ยวกับ developer สาวคนหนึ่งที่ใช้ชีวิตในองค์กรท่ามกลางกระแส digital disruption อ่านแล้วสนุกมาก เป็นเรื่องที่ตื่นเต้น และหลาย ๆ dynamic ที่เกิดขึ้นก็คล้ายกับที่ผมพบเจอในบริษัทซอฟต์แวร์ เลยทำให้

By Chokchai Phatharamalai
Distributed Systems Concepts - System Design, Simply Explained

Distributed Systems Concepts - System Design, Simply Explained

ในช่วงแรก Product เราสามารถออกแบบระบบให้อยู่ใน Server และ Database เดียวกันได้ แต่เมื่อระบบโตขึ้น มีผู้ใช้งานเพิ่มขึ้น ข้อมูลเยอะขึ้น ระบบเริ่มทำงานช้าลดเรื่อยๆ สิ่งที่หนีไม่ได้คือการ Decomposition Service ไปเป็น Distributed System เช่น Member Service, Booking Service, Payment

By Boonsong Srithong