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

เริ่มต้นกับ Functional Programming

เริ่มต้นกับ Functional Programming

ก่อนหน้านี้ผมเจอโพสต์หนึ่งถามว่า สำหรับคนที่เคยใช้แต่ OOP จะเริ่มต้นกับ Functional Programming ควรเริ่มยังไงดี ผมชอบคำตอบที่ตัวเองตอบไป เลยอยากเอามาจดเก็บไว้ ฝันร้ายของโปรแกรมเมอร์ ก่อนจะเข้าไปใน programming แต่ละ paradigm ผมอยากเริ่มต้นที่ปัญหาก่อน หนึ่งในปัญหาที่หลอกหลอนโปรแกรมเมอร์มากที่สุด คื

By Chokchai Phatharamalai
Values ของ Framework ต่าง ๆ

Values ของ Framework ต่าง ๆ

ผมหาคุณค่า (Values) ของผมเจอจากการสังเกตความต้องการที่มักจะมาหาผมบ่อย ๆ ทั้งตอนที่มันได้รับการตอบสนองและทำให้ผมรู้สึกดีมาก ๆ รวมถึงตอนที่มันไม่ได้รับการตอบสนองและทำให้ผมปั่นป่วนมาก ๆ พอความต้องการไหนมาเคาะประตูใจผมบ่อย ๆ ผมก็เริ่มพิจารณาว่าความต้องการนี้แหละที่เป็น Value หนึ่งสำหรั

By Chokchai Phatharamalai
Accepted Responsibility

Accepted Responsibility

ช่วงนี้ผมกำลังอ่านหนังสือ Extreme Programming Explained ของ Kent Beck แล้วก็ได้เจอหัวข้อหนึ่งที่กระทบใจ คือ principle ที่เรียกว่า Accepted Responsibility Principle คืออะไร? ในหนังสืออธิบายว่า Principle คือหลักการที่ทำให้ Values ของ XP กลายเป็น Practices ได้

By Chokchai Phatharamalai
The Five Ideals

The Five Ideals

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

By Chokchai Phatharamalai