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

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

Tuple: ปรัชญาของการปูเสื่อ และศิลปะแห่งการไม่ตั้งชื่อ

Tuple: ปรัชญาของการปูเสื่อ และศิลปะแห่งการไม่ตั้งชื่อ

ในโลกของการเขียนโปรแกรม เรามักถูกสอนให้เป็น “นักจัดระเบียบ” เราสร้างคลาส สร้าง Struct ตั้งชื่อตัวแปรให้สื่อความหมาย (Clean Code) แต่บางครั้ง ความเคร่งครัดที่มากเกินไปอาจกลายเป็นพันธนาการที่ทำให้ Code ของเราอุ้ยอ้ายโดยไม่จำเป็น 1. Naming Fatigue: ภาระของการมีตัวตน ลองนึกภาพคุณได้

By Santi
The Art of Early Return: วินัยแห่งการ “คัดออก” เพื่อสมองที่โล่งกว่าเดิม 10 เท่า

The Art of Early Return: วินัยแห่งการ “คัดออก” เพื่อสมองที่โล่งกว่าเดิม 10 เท่า

ในโลกของการพัฒนาซอฟต์แวร์ เรามักจะถูกสอนให้เป็นคนรอบคอบ ให้คิดถึงความเป็นไปได้ให้ครบทุกด้าน แต่บ่อยครั้งที่ “ความรอบคอบ” นั้นกลับกลายเป็นกับดักที่สร้างความซับซ้อนจนเราเองก็รับมือไม่ไหว วันนี้ผมอยากจะหยิบยกปรัชญาหนึ่งที่ผมพบจากการเขียนโปรแกรม โดยเฉพาะในภาษาอย่าง Rust ซึ่งมันไม่

By Santi
The Logic Trap: เมื่อ “ความถูกต้อง” กลายเป็นอาวุธที่ทำลายทีมซอฟต์แวร์

The Logic Trap: เมื่อ “ความถูกต้อง” กลายเป็นอาวุธที่ทำลายทีมซอฟต์แวร์

ในโลกของการพัฒนาซอฟต์แวร์ เราถูกสอนให้เทิดทูน Logic เป็นพระเจ้า เราใช้เหตุผลในการคัดเลือก Stack, ใช้ความถูกต้องในการทำ Code Review และใช้ตัวเลขในการวาง Roadmap แต่เคยสงสัยไหมครับ? ทั้งที่เราพูดเรื่องที่ “ถูกต้อง” และเป็น “ความจริง” ทุกประการ ทำไมผลลัพธ์ในห้องประชุ

By Santi
Change Management ต้องทำไหมนะ แล้วทำตอนไหน

Change Management ต้องทำไหมนะ แล้วทำตอนไหน

เนื่องจากช่วงนี้ได้ทำงานกับลูกค้าที่มีการเปลี่ยนแปลงทาง scope ของงานเยอะมาก อารมณ์แบบตอน baseline เป็นแบบนึง พอจะเลือกงานมาทำจริงๆ เรียกว่าเปลี่ยนไปตาม strategy ขององค์กรเลยก็ว่าได้ ในฐานะที่เราเป็นกลุ่มนักพัฒนา ที่ยังจำเป็นต้องควบคุมงบประมาณ กำหนด scope และต้องตอบให้ได้ว่า

By Thanthiya Phatharamalai