Co-Product ในการเขียนโปรแกรม | Category Theory for Programming Part 2
Co-Product ในการเขียนโปรแกรม | Category Theory for Programming Part 2
Tue Feb 01 2022
muitsfriday.dev
Co-Product ในการเขียนโปรแกรม | Category Theory for Programming Part 2
Tue Feb 01 2022
จาก Part ที่แล้วเราได้รู้จักกับ Product ไครยังไม่อ่านคลิกๆ ในทฤษฎีแคตากอรี่จะมีการกระทำแบบนึงที่เราจะพบเจอได้บ่อย
โดยการกระทำนั้นต้องเริ่มจากเรามีโครงสร้าง/รูปแบบของแคตากอรี่ที่เราสนใจก่อน จากนั้นให้กลับลูกษร(morphism)เป็นทางตรงกันข้าม ในบางครั้งเราจะได้โครงสร้างที่มีคุณสมบัติน่าสนใจขึ้นมาด้วย
โครงสร้างที่ถูกสร้างขึ้นมาจากการสลับด้านมอฟิซึม เรามีชื่อเรียกให้มันล้ำๆ ว่า “duality” (ดูอัลลิตี้) ของโครงสร้างเดิม
จากบทความตอนที่ผ่านมา เราก็ได้รู้จักรูปแบบนึงที่เรียกว่า product(โปรดักต์) มาแล้ว เมื่อเราสลับด้านมอฟิซึม เราจะได้ดูอัลลิตี้ของโปรดักต์ โดยทั่วไปแล้วเรามักจะเรียกดูอัลลิตี้ของรูปแบบด้วยการเติม co- (โค) นำหน้าชื่อรูปแบบเดิม
ดูอัลลิตี้ของ product ก็คือ co-product เองจ๊ะ
จากที่เกริ่นมาเบื่องต้น โครงสร้างแบบโคโปรดักต์จะมีหน้าตาแบบนี้
ทบทวนนิดนึง ในภาษาโปรแกรมเราจะโฟกัสที่ Category of Type ที่อ็อปเจ็กต์คือชนิดของค่า และมอฟิซึมคือฟังก์ชันที่แปลงชนิดของค่านึงไปเป็นอีกชนิดนึง
คราวนี้ความหมายของ co-product จากแผนภาพคือ มีอ็อปเจ็กต์(ชนิดของค่า) C ที่โดนแปลงมาจากอ็อปเจ็กต์ A หรือ B ได้
ในภาษาโปรแกรมเราจะรู้จักความสามารถนี้กันในนามของ union type หรือ either เป็นชนิดของค่าที่ถูกสร้างขึ้นมาจากขนิดอื่นที่มากกว่า 1 ตัวได้
เรามาดูตัวอย่างกันใน typescript กันดีกว่า ใน typescript เราสามารถเรียกใช้ union type ได้ด้วยการใช้เครื่องหมาย | คั่นระหว่าง 2 type ที่เราจะผสมเข้าไว้ด้วยกัน
type EitherNumberOrString = number | boolean
EitherNumberOrString เป็นชื่อเล่น(alias) ของ union type ที่รวมกันระหว่าง number และ boolean ตัว EitherNumberOrString เองเป็นได้ทั้ง number และ boolean
ถ้าหากอิงจากตามแผนภาพอ็อปเจ็กต์ A
จะเป็น number
ส่วน B
ก็คืออ็อปเจ็กต์ boolean
ใน typescript ทั้งคู่สามารถแปลงเป็น EitherNumberOrString ได้ด้วยการ assign เข้าไปตรงๆ ไม่ต้องมี function อะไรช่วยพิเศษ
let m : EitherNumberOrString = true
let n : EitherNumberOrString = 10
ตรงนี้แม้จะไม่ได้ใช้ function แต่ก็มองว่าเป็นการ เปลี่ยนของ type ได้นั่นคือเป็นตัวแทนของเป็นมอฟิซึ่มในแผนภาพ
โปรดักต์เองยังซ้อนกันได้ ทำไมพี่น้องของโปรดักต์อย่าง โคโปรดักต์จะซ้อนบ้างไม่ได้ เราสามารถมองให้โครงสร้างแบบโคโปรดักต์ซ้อนกันเป็นชั้นลึกเข้าไปได้ดังภาพด้านล่างนี้
ไม่ว่าเราจะเอา co-product ไว้ด้านซ้ายหรือขวา ความหมายของ union ก็มีค่าเท่าเดิม (isomorphic กัน)
type FlexType = number | boolean | string
เรามีชื่อเรียกให้ type ที่ถูกสร้างจากโมเดล product ว่า product type และ co-product ว่า sum type แปลเป็นไทยตรงๆ ก็คือ ชนิดตัวแปลแบบคูณและแบบบวก ถึงตรงนี้อาจจะสงสัยแล้วว่าทำไมถึงได้ชื่อนี้ เราจะมาดูกันแต่ก่อนอื่นเพื่อเป็นตัวอย่างเราจะเริ่มจากให้มี type เริ่มต้นเอาไว้สองประเภทก่อนคือ (เป็นชนิดที่สมมติขึ้นมา)
ตัวอย่างเราจะใช้ type สองอันนี้นะ
จากนั้นเราลองมาสร้าง type ที่เกิดจากการโปรดักต์กันก่อน
สมมติให้ C เป็น product type ที่เกิดจากการรวมของ A และ B จะได้ว่า C บรรจุค่าของ A และ B เอาไว้ในตัวจากบทความ part 1
interface C<A, B> {
a: A;
b: B;
}
ค่าที่เป็นไปได้ของ C จะมีได้ 6 ค่าตามตารางนี้
a1 | a2 | |
---|---|---|
b1 | (a1, b1) | (a2, b1) |
b2 | (a1, b2) | (a2, b2) |
b3 | (a1, b3) | (a2, b3) |
จะเห็นว่าค่าที่เป็นไปได้มีจำนวนเท่ากับ ค่าที่เป็นไปได้ทั้งหมดในชนิด A คูณกับ B (2 x 3 = 6) เป็นที่มาของชื่อ product type
Note: สำหรับคนที่สงสัยเรื่องความเกี่ยวข้องกับผลคูณคาทีเชียนของเซ็ต มันก็คือโครงสร้างโปรดักต์ของเซ็ตแคตากอรี่เช่นเดียวกันครับ
มาดูเรื่อง co-product กันต่อ สมมติให้ D เป็น type ที่เกิดจากการรวมแบบ co-product ของ A B หน้าตาของ D ก็จะเป็นแบบนี้
type D = A | B
ค่าที่เป็นไปได้ทั้งหมดของ D คือค่าของ A และ B รวมกัน คือ a1 a2 b1 b2 b3
ทั้งหมด 5 ตัว
จำนวนค่าที่เป็นไปได้เกิดจากจำนวน type ย่อยรวมกัน เป็นที่มาของชื่อ sum type
ตรงนี้จะรวบรวมคำถามที่เคยโดนถามมา และคำถามที่ผมเองคิดว่าทุกคนน่าจะสงสัยเอาไว้นะครับ
Q: ภาษาที่เป็น dynamic typing เช่น javascript จะไม่มี co-product หรือเปล่า
A: ไม่ใช่ครับ ให้เรามองว่าภาษานั้นตัวแปรที่รับส่งไปมาคือ type any ครับ ซึ่ง any คือ sum type ของทุกๆชนิดมาแล้วครับ
Q: ถ้า type A ค่าที่เป็นไปได้คือ x, y และ type B ค่าที่เป็นไปได้ คือ y, z แล้ว sum type ที่เกิดจากการรวมของ A B จำนวนค่าที่เป็นไปได้คือ 3 ไม่ใช่ 4 มันก็จะไม่ใช่ผลรวมแล้วหรือเปล่า?
A: จำนวนค่าที่เป็นไปได้คือ 4 ครับ เพราะทางทฤษฎี y ของ A และ y ของ B ถือเป็นคนละค่ากันครับ
Q: ถ้าภาษาไม่ซัพพอร์ตการสร้าง union type จะทำยังไงดี
A: เราสามารถใช้ท่าคล้ายๆแบบนี้ได้ครับ (อันนี้เป็น sudo code ที่พยายามทำให้ดูเหมือน java นะครับ)
class C {
String type;
Int a;
Boolean b;
}
C myObject = new C();
myObject.type = "Int"
myObject.a = 10
C myObject = new C();
myObject.type = "Boolean"
myObject.b = true
อาจจะดูลำบากหน่อย แต่ตอนนี้ C เป็นได้ทั้ง int และ boolean เลยละครับด้วยการดู .type
แต่ว่าภาษาที่ไม่ได้ออกแบบ union type มาก็จะไม่ได้ support
เครื่องมือในส่วนนี้มากเช่น เราไม่ควรต้องกำหนด type ด้วยมือเอง หรือเวลาตรวจว่าค่าของ C เป็นชนิดอะไรก็ต้องเช็กเทียบด้วยมือ ถ้าเป็นภาษาที่ถูกสร้างมาโดยมีแนวคิด
union type ตั้งแต่แรกจะสบายกว่านี้ครับ