ผมพยายามทำความเข้าใจ และวิเคราะห์ point พี่ดีน พอพูด categorical term แล้วมันอดไม่ได้ เพราะมันมักจะมีอะไรน่าสนใจซ่อนอยู่เสมอ เมื่อเรามองอะไรผ่าน lense ของ category theory
ก่อนจะไปต่อ ผมอยากเซตพื้นให้พูดคำเดียวกัน มีความหมายเดียวกันก่อน เพราะผมว่าผมไม่แน่ในว่าที่คุยกัน เราเข้าใจแต่ละคำเหมือนกันมั้ยนะ
category ประกอบไปด้วย
- object (eg. type)
- morphism (eg. function)
- identity morphism (eg. identity function)
แล้วต้องสามารถ compose morphism ได้เสมอ (eg. function composition)
เอาไอของ eg. มารวมกัน จะได้ category ของ programming language แบบลวกๆ มาอันนึง เอาเป็น category TS ก็แล้วกันสำหรับวันนี้ (ลวกๆ เพราะมันจะ sound บน context ที่เราจะคุยกัน ก็ต่อเมื่อทุก side-effect มัน represent ใน type)
ทีนี้ functor คือ การ map จาก category นึงไปอีก category นึง โดยทุกๆ object และ morphism ใน category ต้นทางเนี่ย จะต้องมี representative ที่ category ปลายทางเสมอ
endo แปลว่าตัวเอง เพราะงั้น endofunctor มันคือ functor แบบ F : TS -> TS
เช่น Result<A> = Failure string | Success A เป็น endofunctor
เพราะ Result<A> เนี่ย A คือ type ใดๆ ใน TS และ
ทุกๆ f: A => B มี result.map(f: A => B) เสมอ
ทีนี้ quote จานดีน
Compose ลำบากเพราะ ตัวอย่าง throw exception ซึ่งมันเสียความเป็น endufunctor ไปแล้ว ถ้าเป็น endufunctor มันจะ return type เดิมเสมอ แม้จะ error
ผมเข้าใจว่า พี่ดีนกำลังบอกว่ามันจะ return Result เสมอ แต่ถ้า throw มันจะไม่เห็น Result
ทำให้หลังจากนี้มัน compose ไม่ถูก เพราะไม่รู้ว่า compose ไปแล้วมีอะไร throw exception บ้างรึเปล่า ไม่มีใครบอก
ทำให้เอาความสามารถของ monoid ไป compose ต่อ ผ่านการ map/fold ได้อีกทีครับ
monoid ลองดูรูปนี้
อันนี้คือ Int กับการบวก Int ใดๆ +0 ก็ได้ Int, +1 ก็ได้ Int ไปเรื่อยๆ ไม่มีวันจบ นี่คือ monoid และมันเป็นรากฐานของ composability เพราะการที่มันกลับมาเป็น type เดิมได้ จะ compose มันไปอีกเท่าไหร่ก็ได้
function composition ก็เหมือนกัน คือ มี function เอาไป compose กับ function ใดๆ ก็ได้ function
ถ้าอธิบาย monoid ใน programming context มันต้องมีองค์ประกอบ
คือ type T, function f, และ e ที่เป็นค่าค่านึงที่มี type T และมีคุณสมบัติดังนี้ (เขียนแบบ ts แล้วกันนะ)
- closure
type f = (a: T, b: T) => T
- associativity
f(a, f(b, c)) == f(f(a, b), f(c))
- identity
f(a, e) == f(e, a) == a
ซึ่งผมไม่แน่ใจว่าในบริบทนี้ จานดีนหมายถึงอะไรที่เป็น monoid
แต่ผมเข้าใจเอาว่า map คือผลของความเป็น functor เอาไป compose function ต่อได้ด้วยการ
result.map(f).map(g) เพราะมันเท่ากับ result(compose(g, f))
ส่วน fold เนี่ย คือถ้า data structure บางอย่าง อย่างเช่น list, tree อะไรพวกนี้ที่มัน foldable
แล้วใส้ในมันมีความเป็น monoid มันก็จับของเอามารวมกันจนกลายเป็น 1 element ได้ แต่แค่ผมยังไม่เก็ตว่าตรงไหนที่เป็น monoid
