4 Categoryทั้งเล็กและใหญ่ (Draft)
คุณสามารถที่จะเห็นประโยชน์ของcategoryต่างๆโดยการศึกษาตัวอย่างที่หลากหลายของมัน categoryมาในทุกรูปแบบและขนาดและมักจะโผล่ขึ้นมาในที่ที่ไม่คาดถึง เราจะมาเริ่มจากบางอย่างที่เรียบง่ายมากๆ
4.1 ไม่มีวัตถุอยู่
categoryที่ตรงตัวที่สุดคือตัวที่ไม่มีวัตถุอยู่เลยและดังนั้นจึงไม่มีmorphismอยู่เลย มันเป็นcategoryที่น่าเสร้าในตัวมันเองแต่มันอาจจะมีความสำคัญในบริบทของcategoryอิ่นๆ ตัวอย่างเช่น อยู่ในcategoryของทุกๆcategory(แน่นอนว่ามันมีอยู่) ถ้าคุณคิดว่าsetว่างนั้นสมเหตุสมผล แล้วทำไมจึงไม่มีcategoryว่างจึงไม่ละ
4.2 Graphอย่างง่าย
คุณสามารถที่จะสร้างcategoryโดยการแค่ต่อวัตถุต่างเข้าด้วยกันโดยลูกศร คุณสามารถที่จะจินตนาการโดยเริ่มด้วยgraphที่ระบุทิศทาง (directed graph) และทำมันให้เป็นcategoryโดยนำลูกศรมาเพิ่ม อย่างแรกทำการเพิ่มลูกศร identity แล้วก็ในทุกๆลูกศรสองตัวที่ปลายของตัวหนึ่งตรงกับจุดเริ่มต้นของอีกอัน (หรือในอีกคำหนึ่งลูกศรสองตัวที่สามารถประกอบกันได้(composable)) เราก็จะต้องเพิ่มลูกศรเข้ามาในฐานะการประกอบกันของพวกมัน ในทุกๆครั้งที่คุณเพิ่มลูกศรใหม่เข้ามาคุณก็จะต้องพิจารณาการประกอบของมันกับลูกศรอื่นๆด้วย (ยกเว้นสำหรับลูกศร identity) และกับตัวมันเอง คุณจะมักที่จะมีจำนวนลูกศรmujไม่จำกัดแต่นั่นก็ไม่เป็นไร
ในอีกวิธีในการมองของกระบวนการนี้คือคุณกำลังสร้างcategoryที่มีวัตถุเป็นทุกๆจุดในgraphและลำดับต่อๆกัน(chain)ของการเชื่อมที่ สามารถประกอบกันได้ของgraphในฐานะmorphisms (คุณสามารถที่จะคิดว่าmorphism identityในการเชื่อมกันที่มีความยาวเป็นศูนย์)
categoryแบบนี้จะถูกเรียกว่าcategory อิสระ (free) ที่ถูกสร้างมาจากgraphที่ให้มา นี่คือตัวอย่างของการสร้างแบบอิสระ (free construction) ที่เป็นกระบวนการในการทำให้โครงสร้างที่มีอยู่แล้วสมบูรณ์ขึ้นโดยการขยายมันด้วยจำนวนชิ้นที่น้อยที่สุดเพื่อที่จะให้เป็นไปตามกฎของมัน (ในที่นี้คือฎของcategory) เราจะเห็นตัวอย่างนี้มากขึ้นในอนาคต
4.3 Orders (ลำดับ)
และในตอนนี้มีไว้สำหรับอะไรบางอย่างที่แตกต่างออกไปอย่างสิ้นเชิง categoryที่morphismคือความสัมพันธ์ระหว่างobjectในที่คือความสัมพันธ์ของความน้อยกว่าหรือเท่ากับ เรามาลองตรวจสอบดูว่ามันคือcategoryจริงๆหรือไม่ เรามีmorphism identityหรือเปล่า? ทุกๆวัตถุนั้นน้อยกว่าหรือเท่ากับตัวเอง (จริง) เรามีการประกอบกันหรือเปล่า? ถ้า \(a\le b\)และ\(b\le c\)ดังนั้น\(a\le c\) (จริง) แล้วการประกอบกันที่เปลี่ยนหมู่ได้หรือเปล่า (ได้) setที่มีความสัมพันธ์แบบนี้จะถูกเรียกว่า preorder แล้วก็preorderก็เป็นcategory
คุณสามารถที่จะมีความสัมพันธ์ที่เคร่งครัดมากกว่าที่ไปตามเงื่อนไขที่มากขึ้น นั่นก็คือถ้า \(a\le b\)และ\(b\le a\)แล้ว\(a\)ต้องเหมือนกับ\(b\) สิ่งนี้ที่มีเงื่อนไขที่มากขึ้นจะถูกเรียกว่า partial order
สุดท้ายแล้วคุณสามารถที่จะกำหนดเงื่อนไขที่ว่าในวัตถุสองอย่างอย่างไรก็ได้ต้องอยู่ในความสัมพันธ์ ระหว่างกันไม่ว่าทางใดทางหนึ่งก็ตามก็นำไปสู่linear orderหรือtotal order
เรามาลองบรรยายลักษณะของsetที่เป็นลำดับในฐานะcategory ที่ที่preorderคือcategoryที่มีmorphismไม่มากกว่าหนึ่งที่มาจากวัตถุ\(a\)ตัวไหนก็ได้ไปยังวัตถุ\(b\)ตัวไหนก็ได้ อีกชื่อหนึ่งสำหรับcategoryแบบนี้คือ “บาง” (thin) preorderนั้นคือcategoryที่บาง
setของmorphismจากวัตถุ\(a\)ไปยังวัตถุ\(b\)ในcategory\(\textbf{C}\)จะถูกเรียกว่าhom-setและจะถูกเขียนว่า\(\textbf{C}(a,b)\) (หรือในบางครั้ง\(\mathbf{Hom}_{\textbf{C}}(a, b)\)) ดังนั้นทุกๆhom-setในpreorderก็จะเป็นแค่setว่างหรือsetที่มีสมาชิกเพียงตัวเดียว นี่ก็รวมถึงhom-set\(\textbf{C}(a,a)\) ที่เป็นsetจาก\(a\)ไปยัง\(a\)ที่จำเป็นที่จะเป็นsetที่มีสมาชิกเพียงตัวเดียวอย่างidentityในpreorderใดๆก็ตาม แต่คุณอาจจะมีวงจร(cycle)ในpreorder แต่วงจรจะไม่มีอยู่ในpartial order
มันเป็นสิ่งสำคัญที่จะเห็นpreorder partial orderและtotal orderเพราะว่าalgorthmในการเรียงลำดับอย่าง quicksort, bubble sort, merge sort และอื่นๆ สามารถที่จะทำงานได้อย่างถูกต้องแค่ในtotal order และpartial orderสามารถที่จะถูกเรียงลำดับผ่านการเรียงลำดับแบบtopological
4.4 Monoid ในฐานะ Set
Monoidเป็นconceptที่ง่ายมากๆแต่เป็นสิ่งที่ทรงพลังอย่างไม่น่าเชื่อ มันคือแนวคิดภายใต้เลขคณิตพื้นฐาน ทั้งการบวกและคูณนำไปสู่monoid monoidนั้นอยู่ในทุกที่ของการเขียนโปรแกรม มันโผล่ขึ้นมาในฐานะstring, list, data structuresที่foldได้ (foldable data structures), futuresในการเขียนโปรแกรมแบบconcurrent, event ในการเขียนโปรแกรมแบบfunctionalที่ตอบสนองได้(functional reactive programming)และอื่นๆ
ตามประเพณีแล้ว monoidถูกนิยามในฐานะsetที่มีการคำนวณ(operation)แบบbinary (ก็คือนำค่าสองค่ามาคำนวณรวมกัน) สิ่งที่จำเป็นจากการคำนวณนี้คือว่ามันต้องมีคุณสมบัติของการเปลี่ยนหมู่ (associativity) และมีสมาชิกที่พิเศษที่ทำตัวเหมือนunitของารคำนวณนั้นๆ
ตัวอย่างเช่น จำนวนธรรมชาติที่มีเลขศูนย์เป็นmonoidภายใต้การบวกกัน คุณสมบัติการเปลี่ยนหมู่คือการที่ว่า
\[ (a + b) + c = a + (b + c) \]
(ในอีกความหมายหนึ่งก็คือเราสามารถที่จะละวงเล็บในการบวกกันได้)
สมาชิกที่เป็นnaturalคือเลขศูนย์ก็เพราว่า
\[ 0+a=a \]
และ
\[ a+0=a \]
สมการที่สองนั้นซ้ำซ้อนก็เพราะว่าการบวกกันนั้นมีคุณสมบัติการสลับที่ (associative, \(a+b=b+a\)) แต่คุณสมบัติการสลับที่ไม่ได้อยู่ในนิยามของmonoid ยกตัวอย่างเช่นการเชื่อมต่อกันของstringนั้นไม่มีคุณสมบัติการสลับที่แต่ก็เป็นmonoid สมาชิกnaturalของการเชื่อมต่อกันของstringก็คือstringว่างที่สามารถที่จะผูกติดกับstringข้างไหนก็ได้ในแบบที่ไม่เปลี่ยนแปลงstringมันเอง
ในHaskellเราสามารถที่จะนิยามclassของtypeสำหรับmonoidsได้ เป็นtypeที่มีสมาชิกnaturalที่เรียกว่าmempty
และ การคำนวณแบบbinaryที่เรียกว่าmappend
class Monoid m where
mempty :: m
mappend :: m -> m -> m
ในsignatureของtypeสำหรับfunctionที่มีargumentสองอย่าง m->m->m
อาจจะดูแปลกในตอนแรก แต่มันจะสามารถเข้าใจได้หลังจากที่เราเข้าใจเกี่ยวกับการcurry คุณอาจจะตีความsignatureที่มีลูกศรหลายอันในสองแบบง่ายๆ ในฐานะfunctionที่มีหลายargumentsโดยที่มีtypeด้านขวาสุดเป็นtypeที่จะโดนคืน หรือในฐานะfunctionที่มีargumentอย่างหนึ่ง (ในด้านช้ายสุด)และคืนfunctionออกมา ในการตีความอย่างหลังอาจจะถูกทำให้ชัดเจนมากขึ้นโดยการเพิ่มวงเล็บเข้ามา (ที่ก็อาจจะซ้ำซากเพราะลูกศรนั้นมีคุณสมบัติการเปลี่ยนหมู่ด้านขวา) ในแบบว่าที่ว่า m->(m->m)
เราจะกลับมาในการตีความแบบนี้ในอีกไม่ช้า
อาจจะสังเกตว่าในHaskellไม่มีวิธีการที่จะแสดงคุณสมบัติทางmonoidของmempty
และ mappend
(ก็คือความจริงที่ว่าmempty
นั้นเป็นnaturalและmappend
นั้นมีคุณสมบัติการเปลี่ยนหมู่) มันคือหน้าที่ของโปรแกรมเมอร์ที่จะทำให้มั่นใจได้ว่าเงื่อนไขเหล่านี้เป็นจริง
classในHaskellไม่ได้มีความต้องการผู้เขียนสูงเมื่อเทียบกับclassต่างๆในC++ ในตอนที่คุณนิยามtypeชนิดใหม่คุณไม่จำเป็นต้องที่จะระบุclassนั้นอย่างชัดเจน คุณมีอิสระในการที่จะไม่เขียนก่อนล่วงหน้า (procrastinate) และสามารถประกาศtypeนี้ให้เป็นinstanceของclassบางclassในเวลาถัดๆไป ในฐานะตัวอย่าง เรามาประกาศString
ให้เป็นmonoidโดยการที่เขียนmempty
และmappend
(ที่ในความจริงแล้ว มีเขียนให้คุณอยู่แล้วในPreludeมาตรฐาน)เป็นดังต่อไปนี้
instance Monoid String where
mempty = ""
mappend = (++)
ในที่นี้เราได้นำการคำนวณการต่อกันของlist (++)
มาใช้ช้ำได้เพราะว่าString
ก็คือแค่listของตัวอักษรต่างๆ
ขออนุญาติให้ผมได้ทำการอธิบายsyntaxของHaskellว่า ในการคำนวณแบบinfixสามารถที่จะถูกเปลี่ยนให้เป็นfunctionที่มีสองargumentโดยการครอบมันด้วยวงเล็บ ถ้ามีstringสองตัวคุณสามารถที่จะต่อมันเข้ามาด้วยกันโดยการแทรก ++
ระหว่างพวกมัน
"Hello " ++ "world!"
หรือโดยการนำสองargumentsเข้ามาให้กลับการวงเล็บ (++)
อย่าง
++) "Hello " "world!" (
สังเกตได้ว่าarugmentของfunctionไม่ได้แยกจากกันโดยลูกน้ำหรือถูกครอบโดยวงเล็บ (นี่อาจจะเป็นสิ่งที่ยากที่สุดในการทำความเคยชินในตอนเรียนภาษาHaskell)
แต่ก็คุ้มที่จะเน้นว่าHaskellอนุญาตให้คุณแสดงความเท่ากันของfunctionในแบบนี้
mappend = (++)
ตามแนวคิดแล้วนี้แตกต่างเมื่อเทียบกับการแสดงถึงความเท่ากันของค่าต่างๆที่ถูกผลิตโดยfunctionในตัวอย่างที่ว่า
mappend s1 s2 = (++) s1 s2
อย่างตัวแรกสามารถที่จะถูกแปลให้เป็นความเท่ากันของmorphismในcategory \(\textbf{Hask}\) (หรือ\(\textbf{Set}\)ถ้าเราจะไม่สนใจbottomที่ก็คือชื่อสำหรับการคำนวณที่ไม่มีที่สิ้นสุด) สมการแบบนี้ไม่ได้แค่มีความรวบรัดที่ไม่เยินเย้อแต่ก็สามารถถูกทำการgeneralizeในบ่อยครั้งกับcategoryอื่นๆได้ด้วย ในสิ่งอย่างหลังจะถูกเรียกว่าความเท่ากันในส่วนขยาย (extensional equality) ที่สื่อความจริงที่ว่า สำหรับทุกstringแบบไหนก็ได้ ผลลัพธ์ของ mappend
และ (++)
จะเหมือนกัน เนื่องด้วยว่าค่าต่างๆของargumentในบางครั้งจะถูกเรียกว่าpoints (จุด) (ในแบบที่ว่าค่าของ\(f\)ในจุดของ\(x\)) นี่เรียกว่าความเท่ากันในแต่ละจุด (point-wise equality) ความเท่ากันของfunctionโดยที่ไม่ได้ระบุถึงargumentจะถูกเรียกว่า point-free (อนึ่งสมการที่เป็นpoint-freeมักจะมีการประกอบกันของfunction ที่จะถูกเขียนด้วยสัญลักษณ์ของจุด(point)ดังนั้นนี่อาจจะเป็นสิ่งที่สับสนสำหรับผู้เริ่มต้น)
สิ่งที่ใกล้ที่สุดของการประกาศmonoidในC++อาจจะต้องใช้ feature conceptมาตราฐานของC++20อย่าง
template<class T>
struct mempty;
template<class T>
(T, T) = delete;
T mappend
template<class M>
concept Monoid = requires (M m) {
{ mempty<M>::value() } -> std::same_as<M>;
{ mappend(m, m) } -> std::same_as<M>;
};
นิยามแรกคือโครงสร้างที่ควรที่จะยึดสมาชิกnaturalไว้ในทุกๆกรณี
delete
ที่เป็นkeywordหมายความว่ามันไม่มีค่าเริ่มต้นถูกนิยามไว้ โดยที่มันจะถูกระบุเป็นรายกรณีไป โดยคล้ายๆกันแล้ว ก็ไม่มีค่าเริ่มต้นสำหรับ mappend
concept
ของMonoid
ทดสอบว่าได้มีคำนิยามที่ถูกต้องของmemepty
และ mappend
หรือไม่ สำหรับtypeM
ที่ถูกให้มา
การสร้าง(instantiation)ของconceptของMonoid
สามารถที่จะมาคู่กับการเขียนการspecializationsและoverloadsที่ถูกต้อง
<>
templatemempty<std::string> {
struct std::string value() { return ""; }
static
};
<>
templatestd::string mappend(std::string s1, std::string s2) {
return s1 + s2;
}
4.5 MonoidในฐานะCategory
นั้นคือนิยามที่“คุ้นเคย”ของmonoidในความหมายของสมาชิกของset แต่คุณรู้ก็รู้อยู่แล้วว่าในทฤษฎีcategory เราพยายามที่จะหลีกหนีจากsetและสมาชิกของมันแล้วมาพูดเกี่ยวกับวัตถุและmorphismแทน ดังนั้นเรามาเปลี่ยนมุมมองเล็กน้อยและคิดถึงการใช้งานของoperator binaryในฐานะ”การเคลื่อน”หรือ”การขยับ”ของสิ่งต่างๆในset
ตัวอย่างเช่นการคำนวณของการบวกด้วยเลขห้าสำหรับทุกๆจำนวนธรรมชาติ มักก็จะจับคู่\(0\)ไปยัง\(5\) \(1\)ไปยัง\(6\) \(2\)ไปยัง\(7\)และต่อไปๆ นั่นคือfunctionที่ถูกนิยามในsetของจำนวนธรรมชาติ นั่นก็ดี เรามีfunctionและset โดยทั่วไปแล้วสำหรับทุกๆตัวเลข\(n\)มักจะมีfunctionของการบวก\(n\)เข้าไป (หรือก็คือตัวบวก(adder)ของ\(n\))
แล้วตัวบวกของ\(n\)จะประกอบกันอย่างไร? การประกอบกันของfunctionที่บวก\(5\)กับfunctionที่บวก\(7\)คือfunctionที่บวก\(12\) ดังนั้นการประกอบกันของตัวบวกสามารถทำให้เท่ากันได้กับกฏของการบวกกัน นั่นก็ดีเหมือนกัน เราสามารถที่จะแทนที่การบวกกันโดยการใช้การประกอบกันของfunction
แต่เดี๋ยวก่อนมันมีมากกว่านั้น ได้มีตัวบวกสำหรับสมาชิกnaturalอย่างเลขศูนย์ การบวกด้วยศูนย์ไม่ได้เคลื่อนอะไรเลยดังนั้นมันคือfunction identityในsetของจำนวนธรรมชาติ
แทนที่จะให้กฏทั่วๆไปของการบวก ผมสามารถที่จะให้กฏของการประกอบกันของตัวบวกโดยที่ไม่สูญเสียข้อมูลอะไรทั้งสิ้นเช่นกัน สังเกตว่าการประกอบกันของตัวบวกนั้นมีคุณสมบัติการเปลี่ยนหมู่ก็เพราะว่าการประกอบกันของfunctionนั้นมีคุณสมบัติการเปลี่ยนหมู่ และเรามีตัวบวกศูนย์ที่ตรงกันกับfunctionidentity
ผู้อ่านที่มีไหวพริบอาจจะเล็งเห็นว่าการจับคู่กันระหว่างจำนวนเต็มกับตัวบวกตามมาจากการเขียนแบบที่สองของsignatureของtypeของmappend
ว่า m->(m->m)
มันบอกว่าmappend
จับคู่กันของสมาชิกของsetในแบบmonoidและfunctionที่กระทำบนsetนั้น
ในตอนนี้ผมต้องการให้คุณที่จะลืมว่าคุณกำลังทำงานกับsetของจำนวนธรรมชาติและคิดเกี่ยวกับมันในฐานะวัตถุเดี่ยว เป็นชิ้นที่มีmorphismหลายอัน (ซึ่งก็คือตัวบวก) monoidคือcategoryที่มีวัตถุชิ้นเดียว ความจริงที่ว่าmonoidมาจากภาษาGreekว่าmono ที่หมายความว่าการเป็นเดี่ยว ในทุกๆmonoidสามารถที่จะถูกอธิบายในฐานะcategoryที่มีวัตถุชิ้นเดียวที่มีsetของmorphismที่อยู่ภายใต้กฏของการประกอบกัน
การต่อกันของstringก็เป็นกรณีที่น่าสนใจเพราะว่าเรามีทางเลือกของการนิยามตัวต่อกันทางในทางด้านขวา(right appenders)และตัวต่อกันทางด้านช้าย(left appendersหรือสามารถที่ถูกเรียกว่า prependerถ้าคุณต้องการ) ตารางของการประกอบกันของทั้งสองรูปแบบก็เป็นเหมือนเงากระจกสะท้อนระหว่างกัน คุณสามารถที่จะโน้มน้าวตัวเองว่าการต่อกันของ”bar”กับ”foo”นั้นตรงกันกับการต่อกันข้างหน้า (prepending)ของ “foo”หลังการต่อกันข้างหน้าของ “bar” คุณอาจจะถามว่าคำถามว่าในทุกๆmonoidที่เป็นcategory (categoryที่มีหนึ่งวัตถุ)นิยามmonoidที่เป็นsetกับoperatorที่เป็นbinaryอย่างเป็นเอกลักษณ์ได้หรือเปล่า? จริงๆแล้วเราสามารถที่จะสกัดsetออกจากcategoryที่มีหนึ่งวัตถุ setตัวนี้คือsetของmorphism (ในตัวอย่างของเราก็คือตัวบวก) ในอีกคำๆหนึ่งเรามีhom-set \(\textbf{M}(m,m)\) ของวัตถุเดี่ยว\(m\)ในcategory\(\textbf{M}\) เราสามารถที่จะนิยามoperationที่เป็นbinaryอย่างง่ายดายในsetที่ก็คือ ผลของproductแบบmonoidระหว่างสองสมาชิกในsetนั้นตรงกันกับ morphismที่เป็นเป็นการประกอบกันของสองmorphismในการทำproduct ถ้าคุณให้สมาชิกสองตัวของ\(\textbf{M}(m,m)\)ที่ตรงกันกับ \(f\)และ\(g\) productของทั้งสองจะตรงกันกับการประกอบกันของ\(f\circ g\) การประกอบนั้นเป็นไปได้ตลอดเพราะว่าสิ่งเริ่มต้นและปลายทางของmorphismเหล่านี้เป็นวัตถุเดียวกัน
และการมีกฎของการเปลี่ยนหมู่ก็อยู่ในกฎของcategory morphism identityก็คือสมาชิกnaturalของproductนี้ ดังนั้นเราสามารถที่จะ กู้setที่เป็นmonoidจากcategoryที่เป็นmonoidได้ตลอด สำหรับทุกๆความตั้งใจและวัตถุประสงค์ สองสิ่งนี้เป็นสิ่งเดียวกัน
แต่ก็มีจุดเล็กน้อยที่นักคณิตศาสตร์อยากจะแย้งนั้นก็คือmorphismอาจจะไม่ก่อให้เกิดset ในโลกของcategoryได้มีสิ่งของที่ใหญ่กว่าset categoryที่morphismระหว่างสองวัตถุใดๆก็ได้ก่อให้เกิดsetจะถูกเรียกว่าlocally small ตามที่สัญญาไว้แล้วผมจะเพิกเฉยต่อรายละเอียดปลีกย่อยนี้ แต่ผมคิดว่าผมต้องกล่าวถึงเพื่อที่ให้คุณได้รู้
มีปรากฏการณ์ที่น่าสนใจหลายอย่างในทฤษฎีcategoryที่มีรากฐานในความจริงที่ว่า สมาชิกของhom-setสามารถที่จะถูกมองในฐานะทั้งmorphismที่ตามกฏของการประกอบกัน และในฐานะจุดในsetที่ในที่นี้การประกอบกันของmorphismใน\(\textbf{M}\)ถูกแปลไปสู่productแบบmonoidalในsetของ\(\textbf{M}(m,m)\)
4.6 โจทย์ท้าทาย
- ลองสร้างcategoryที่อิสระจาก
- graphที่มีจุดเดียวและไม่มีตัวเชื่อม
- graphที่มีจุดเดียวและตัวเชื่อมอันเดี่ยวที่มีการระบุทิศทาง (ใบ้ว่า ตัวเชื่อมอันนี้สามารถที่จะถูกประกอบกับตัวมันเอง)
- graphที่มีจุดสองตัวและลูกศรเดี่ยวระหว่างมัน
- graphที่มีจุดเดี่ยวและลูกศร26อันที่มีเครื่องหมายเป็นตัวอักษรของa,b,c,…,z
- แล้วประเภทของorderต่างๆแบบนี้คืออะไรบ้าง
- setของsetที่มีความสัมพันธ์แบบการอยู่ข้างในแบบที่ว่า \(A\)อยู่ใน\(B\)ถ้าทุกๆสมาชิกของ\(A\)นั้นเป็นสมาชิกของ\(B\)
- typeของC++ที่มาคู่กับความสัมพันธ์ของtypeย่อย
T1
คือtypeย่อยของT2
ถ้าpointerไปยังT1
สามารถที่จะผ่านเข้าไปยังfunctionที่คาดหวังกับpointerของT2
โดยที่ไม่ได้นำไปสู่ปัญหาในการcompilation
- ลองคิดว่า
Bool
เป็นsetที่มีค่าสองค่าTrue
และFalse
ลองแสดงว่ามันก่อให้เกิดmonoidสองตัว(ในทางทฤษฎีset)ที่คู่กับการคำนวณของ&&
(and/และ) และ||
(or/หรือ) - ลองเขียนmonoid
Bool
ด้วยการคำนวณแบบandในฐานะcategory ลองเขียนmorphismทั้งหมดและกฏของการประกอบกันด้วย - ลองเขียนการบวกกันแบบmodular \(3\) (addition modulo 3)ในฐานะcategoryแบบmonoid