ใครบอกว่าต่อไปนี้ก่อน?
Monad เป็นเพียง monoid ในประเภท endofunctors ปัญหาคืออะไร
และในบันทึกย่อที่สำคัญน้อยกว่านี่คือความจริงและถ้าเป็นเช่นนั้นคุณสามารถให้คำอธิบายได้ (หวังว่าจะมีคนที่ไม่เข้าใจประสบการณ์ของ Haskell มาก)
ใครบอกว่าต่อไปนี้ก่อน?
Monad เป็นเพียง monoid ในประเภท endofunctors ปัญหาคืออะไร
และในบันทึกย่อที่สำคัญน้อยกว่านี่คือความจริงและถ้าเป็นเช่นนั้นคุณสามารถให้คำอธิบายได้ (หวังว่าจะมีคนที่ไม่เข้าใจประสบการณ์ของ Haskell มาก)
คำตอบ:
ประโยคเฉพาะนั้นคือโดย James Iry จากบทสรุปความบันเทิงที่ไม่สมบูรณ์และไม่ถูกต้องส่วนใหญ่ของประวัติศาสตร์การเขียนโปรแกรมภาษาซึ่งเขาได้กล่าวอ้างถึง Philip Wadler
ข้อความต้นฉบับมาจาก Saunders Mac Lane ในหมวดหมู่สำหรับนักคณิตศาสตร์ทำงานซึ่งเป็นหนึ่งในตำราพื้นฐานของหมวดหมู่ทฤษฎี ที่นี่มีอยู่ในบริบทซึ่งอาจเป็นสถานที่ที่ดีที่สุดในการเรียนรู้สิ่งที่มันหมายถึง
แต่ฉันจะแทง ประโยคเดิมคือ:
ทั้งหมดบอกว่า monad ใน X เป็นเพียง monoid ในหมวดหมู่ของ endofunctors ของ X ด้วยผลิตภัณฑ์×แทนที่ด้วยองค์ประกอบของ endofunctors และหน่วยที่กำหนดโดย endofunctor ตัวตน
Xนี่คือหมวดหมู่ Endofunctors มี functors จากหมวดหมู่เพื่อตัวเอง (ซึ่งมักจะทุก Functor
s เท่าที่โปรแกรมเมอร์ทำงานมีความกังวลเนื่องจากพวกเขากำลังส่วนใหญ่ที่เกี่ยวข้องกับการเพียงหนึ่งหมวดหมู่; หมวดหมู่ของประเภท - แต่ฉันเชือนแช) แต่คุณสามารถจินตนาการหมวดหมู่อื่นซึ่งเป็นหมวดหมู่ของ "endofunctors on X " นี่คือหมวดหมู่ที่วัตถุคือ endofunctors และ morphisms เป็นการเปลี่ยนแปลงตามธรรมชาติ
และในบรรดา endofunctors บางคนอาจเป็นพระ อันไหนเป็นพระ อย่างแน่นอนคนที่มีmonoidalในความหมายเฉพาะ แทนที่จะสะกดการแมปที่ถูกต้องจาก monads ไปยัง monoids (เนื่องจาก Mac Lane ทำได้ดีกว่าที่ฉันคาดหวังมาก) ฉันจะใส่คำจำกัดความตามลำดับและให้คุณเปรียบเทียบ:
* -> *
มีFunctor
อินสแตนซ์)join
ใน Haskell)return
ใน Haskell)กับบิตของ squinting คุณอาจจะสามารถที่จะเห็นว่าทั้งสองของคำนิยามเหล่านี้เป็นกรณีเดียวกันแนวคิดนามธรรม
S
เป็นประเภทสิ่งที่คุณทำได้เมื่อเขียนฟังก์ชั่นf :: () -> S
ก็คือเลือกประเภทของคำเฉพาะS
("องค์ประกอบ" ของมันถ้าคุณต้องการ) และกลับมา มัน ... คุณไม่ได้รับข้อมูลจริงเกี่ยวกับการโต้แย้งดังนั้นจึงไม่มีวิธีที่จะเปลี่ยนแปลงพฤติกรรมของฟังก์ชัน ดังนั้นf
จะต้องมีฟังก์ชั่นคงที่ซึ่งเพียงแค่คืนสิ่งเดิมทุกครั้ง ()
("หน่วย") เป็นวัตถุเทอร์มินัลของหมวดหมู่Haskและมันไม่ใช่เรื่องบังเอิญว่ามีค่า 1 (ไม่แตกต่างกัน) ที่อาศัยอยู่
โดยสังหรณ์ใจฉันคิดว่าสิ่งที่คำศัพท์ทางคณิตศาสตร์แฟนซีพูดคือ:
หนังสือคือชุดของวัตถุและวิธีการรวมพวกเขา monoids ที่รู้จักกันดีคือ:
มีตัวอย่างที่ซับซ้อนกว่านี้ด้วย
นอกจากนี้monoid ทุกตัวจะมีตัวตนซึ่งเป็นองค์ประกอบ "no-op" ที่ไม่มีผลเมื่อคุณรวมเข้ากับสิ่งอื่น:
ในที่สุด monoid ต้องเชื่อมโยงกัน (คุณสามารถลดชุดค่าผสมที่มีความยาวได้ตามที่คุณต้องการตราบใดที่คุณไม่เปลี่ยนลำดับจากซ้ายไปขวาของวัตถุ) การเพิ่มคือตกลง ((5 + 3) +1 == 5+ (3+ 1)) แต่การลบไม่ใช่ ((5-3) -1! = 5- (3-1))
ทีนี้ลองพิจารณาชุดพิเศษชนิดและวิธีพิเศษในการรวมวัตถุ
สมมติว่าชุดของคุณมีวัตถุชนิดพิเศษ: ฟังก์ชั่น และฟังก์ชั่นเหล่านี้มีลายเซ็นที่น่าสนใจ: พวกมันไม่ได้นำตัวเลขไปเป็นตัวเลข แต่ละฟังก์ชั่นจะใช้ตัวเลขเป็นรายการตัวเลขในกระบวนการสองขั้นตอนแทน
ตัวอย่าง:
นอกจากนี้วิธีการรวมฟังก์ชั่นของเราเป็นพิเศษ วิธีง่ายๆที่จะรวมฟังก์ชั่นเป็นองค์ประกอบ : ลองตัวอย่างข้างต้นของเราและเขียนแต่ละฟังก์ชั่นด้วยตัวเอง:
ประเด็นสำคัญคือคุณสามารถรวมจำนวนเต็มสองจำนวนเพื่อให้ได้จำนวนเต็ม แต่คุณไม่สามารถเขียนสองฟังก์ชันและรับฟังก์ชั่นประเภทเดียวกันได้เสมอ (ฟังก์ชั่นที่มีประเภท-> a จะเขียน แต่a-> [a]จะไม่ได้)
ดังนั้นขอนิยามวิธีที่แตกต่างของการรวมฟังก์ชั่น เมื่อเรารวมสองฟังก์ชั่นเหล่านี้เราไม่ต้องการที่จะ "ตัดสองครั้ง" ผลลัพธ์
นี่คือสิ่งที่เราทำ เมื่อเราต้องการรวมสองฟังก์ชั่น F และ G เราจะทำตามกระบวนการนี้ (เรียกว่าการผูก ):
กลับไปที่ตัวอย่างของเรามารวมฟังก์ชั่น (ผูก) เข้ากับตัวเองโดยใช้วิธีใหม่ของฟังก์ชั่น "การผูก":
วิธีการรวมฟังก์ชั่นที่ซับซ้อนมากขึ้นนี้คือการเชื่อมโยง (ต่อจากการรวมองค์ประกอบของฟังก์ชั่นเมื่อคุณไม่ได้ทำสิ่งห่อหุ้มแฟนซี)
ผูกมันทั้งหมดเข้าด้วยกัน
มีหลายวิธีในการ "ห่อ" ผลลัพธ์ คุณสามารถสร้างรายการหรือตั้งค่าหรือทิ้งทั้งหมดยกเว้นผลลัพธ์แรกในขณะที่สังเกตว่าไม่มีผลลัพธ์ให้แนบ sidecar of state พิมพ์ข้อความบันทึก ฯลฯ
ฉันเล่นหลวมเล็กน้อยกับคำจำกัดความโดยหวังว่าจะได้ความคิดที่สำคัญข้ามไปได้โดยสัญชาตญาณ
ฉันได้ง่ายสิ่งเล็กน้อยโดยยืนยันว่า monad ของเราดำเนินการในการทำงานประเภท-> [เป็น] ในความเป็นจริง monads ทำงานในฟังก์ชั่นของประเภท-> mbแต่การวางนัยทั่วไปเป็นรายละเอียดทางเทคนิคที่ไม่ใช่ความเข้าใจหลัก
a -> [b]
และc -> [d]
(คุณสามารถทำเช่นนี้หากb
= c
) นี้ไม่ได้ค่อนข้างอธิบายหนังสือ อันที่จริงแล้วมันคือการทำงานแบบแบนที่คุณอธิบายไว้แทนที่จะเป็นองค์ประกอบของฟังก์ชั่นซึ่งเป็น "ตัวดำเนินการโมโน"
a -> [a]
นี้จะเป็น monoid (เพราะคุณจะลดหมวดหมู่ Kleisli เป็นวัตถุเดียวและหมวดหมู่ของวัตถุเดียวเท่านั้น เป็นคำจำกัดความที่ monoid!) แต่มันจะไม่จับภาพทั่วไปของ monad
อย่างแรกคือส่วนขยายและไลบรารีที่เราจะใช้:
{-# LANGUAGE RankNTypes, TypeOperators #-}
import Control.Monad (join)
ของเหล่านี้RankNTypes
เป็นสิ่งเดียวที่จำเป็นอย่างยิ่งต่อไปนี้ ฉันเคยเขียนคำอธิบายRankNTypes
ว่าบางคนดูเหมือนจะมีประโยชน์ดังนั้นฉันจะพูดถึงสิ่งนั้น
การอ้างอิงคำตอบที่ยอดเยี่ยมของ Tom Crockettเรามี:
Monad คือ ...
- endofunctor, T: X -> X
- การเปลี่ยนแปลงตามธรรมชาติ, μ: T × T -> Tโดยที่×หมายถึงองค์ประกอบของ functor
- การเปลี่ยนแปลงตามธรรมชาติη: I -> Tโดยที่ฉันเป็นตัวตนของ endofunctor บนX
... เป็นไปตามกฎหมายเหล่านี้:
- μ (μ (T × T) × T)) = μ (T ×μ (T × T))
- μ (η (T)) = T = μ (T (η))
เราจะแปลสิ่งนี้เป็นรหัส Haskell ได้อย่างไร เรามาเริ่มกันด้วยแนวคิดของการเปลี่ยนแปลงตามธรรมชาติ :
-- | A natural transformations between two 'Functor' instances. Law:
--
-- > fmap f . eta g == eta g . fmap f
--
-- Neat fact: the type system actually guarantees this law.
--
newtype f :-> g =
Natural { eta :: forall x. f x -> g x }
ชนิดของรูปแบบf :-> g
คล้ายคลึงกับประเภทฟังก์ชั่น แต่แทนที่จะคิดว่ามันเป็นฟังก์ชั่นระหว่างสองประเภท (ประเภท*
) คิดว่ามันเป็นซึ่มส์ระหว่างสองfunctors (แต่ละชนิด* -> *
) ตัวอย่าง:
listToMaybe :: [] :-> Maybe
listToMaybe = Natural go
where go [] = Nothing
go (x:_) = Just x
maybeToList :: Maybe :-> []
maybeToList = Natural go
where go Nothing = []
go (Just x) = [x]
reverse' :: [] :-> []
reverse' = Natural reverse
โดยทั่วไปใน Haskell การแปลงโดยธรรมชาติคือฟังก์ชั่นจากบางประเภทf x
เป็นอีกประเภทหนึ่งg x
ซึ่งx
ตัวแปรประเภทคือ "ไม่สามารถเข้าถึงได้" ไปยังผู้โทร ดังนั้นสำหรับตัวอย่างเช่นsort :: Ord a => [a] -> [a]
ไม่สามารถทำในการเปลี่ยนแปลงทางธรรมชาติเพราะมันเป็น "จู้จี้จุกจิก" a
เกี่ยวกับประเภทที่เราอาจจะยกตัวอย่างสำหรับ วิธีหนึ่งที่ใช้งานง่ายที่ฉันมักจะใช้ในการคิดสิ่งนี้คือ:
ทีนี้ถ้าอย่างนั้นเรามาจัดการกับส่วนคำจำกัดความกันเถอะ
ประโยคแรกคือ "endofunctor, T: X -> X. " Functor
ใน Haskell ทุกคนเป็น endofunctor ในสิ่งที่ผู้คนเรียกว่า "หมวด Hask" ซึ่งวัตถุประเภท Haskell (ชนิด*
) และมี morphisms เป็นฟังก์ชัน Haskell ฟังดูเหมือนประโยคที่ซับซ้อน แต่จริงๆแล้วมันเป็นเรื่องเล็กน้อย ทั้งหมดหมายความว่าเป็นที่Functor f :: * -> *
ให้คุณสร้างประเภทf a :: *
สำหรับใด ๆa :: *
และฟังก์ชั่นfmap f :: f a -> f b
ออกจากใด ๆf :: a -> b
และสิ่งเหล่านี้เป็นไปตามกฎหมาย functor
ประโยคที่สอง: Identity
functor ใน Haskell (ซึ่งมาพร้อมกับแพลตฟอร์มดังนั้นคุณสามารถนำเข้าได้) ถูกกำหนดด้วยวิธีนี้:
newtype Identity a = Identity { runIdentity :: a }
instance Functor Identity where
fmap f (Identity a) = Identity (f a)
ดังนั้นการเปลี่ยนแปลงตามธรรมชาติη: I -> Tจากคำนิยามของ Tom Crockett สามารถเขียนได้ด้วยวิธีนี้Monad
เช่นt
:
return' :: Monad t => Identity :-> t
return' = Natural (return . runIdentity)
ประโยคที่สาม: องค์ประกอบของสองฟังก์ชั่นใน Haskell สามารถกำหนดได้ด้วยวิธีนี้ (ซึ่งมาพร้อมกับแพลตฟอร์ม):
newtype Compose f g a = Compose { getCompose :: f (g a) }
-- | The composition of two 'Functor's is also a 'Functor'.
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose fga) = Compose (fmap (fmap f) fga)
ดังนั้นการแปลงตามธรรมชาติμ: T × T -> Tจากนิยามของ Tom Crockett สามารถเขียนได้ดังนี้:
join' :: Monad t => Compose t t :-> t
join' = Natural (join . getCompose)
คำแถลงว่านี่เป็น monoid ในหมวดหมู่ของ endofunctors นั้นหมายความว่าCompose
(นำไปใช้เพียงบางส่วนกับพารามิเตอร์สองตัวแรก) นั้นคือการเชื่อมโยงและนั่นIdentity
คือองค์ประกอบตัวตน คือว่ามอร์ฟอสมีมอร์ฟิซึ่มดังต่อไปนี้:
Compose f (Compose g h) ~= Compose (Compose f g) h
Compose f Identity ~= f
Compose Identity g ~= g
สิ่งเหล่านี้ง่ายต่อการพิสูจน์เพราะCompose
และIdentity
ทั้งคู่ถูกกำหนดเป็นnewtype
และรายงาน Haskell กำหนดความหมายของnewtype
การเป็น isomorphism ระหว่างประเภทที่ถูกกำหนดและประเภทของการโต้แย้งไปยังตัวnewtype
สร้างข้อมูลของ ตัวอย่างเช่นลองพิสูจน์Compose f Identity ~= f
:
Compose f Identity a
~= f (Identity a) -- newtype Compose f g a = Compose (f (g a))
~= f a -- newtype Identity a = Identity a
Q.E.D.
Natural
ใหม่ฉันไม่สามารถเข้าใจได้ว่า(Functor f, Functor g)
ข้อ จำกัด กำลังทำอะไร คุณอธิบายได้ไหม
Functor
ข้อ จำกัดเหล่านั้นออกไปเพราะไม่จำเป็น หากคุณไม่เห็นด้วยก็สามารถเพิ่มกลับได้
join
มีการกำหนดไว้ และนั่นjoin
คือมอร์ฟิซึ่มประมาณการ แต่ฉันไม่แน่ใจ.
หมายเหตุ:ไม่นี่ไม่เป็นความจริง เมื่อถึงจุดหนึ่งก็มีความคิดเห็นเกี่ยวกับคำตอบนี้จากแดน Piponi ตัวเองบอกว่าสาเหตุและผลที่นี่ตรงข้ามแน่นอนว่าเขาเขียนบทความของเขาในการตอบสนอง quip ของ James Iry แต่ดูเหมือนว่ามันจะถูกลบออกบางทีอาจจะเป็นความคิดที่เป็นระเบียบ
ด้านล่างคือคำตอบเดิมของฉัน
มันค่อนข้างเป็นไปได้ที่ Iry ได้อ่านจาก Monoids ถึง Monadsซึ่งเป็นตำแหน่งที่ Dan Piponi (sigfpe) สร้าง monads จาก monoids ใน Haskell ด้วยการอภิปรายอย่างมากเกี่ยวกับทฤษฎีหมวดหมู่และกล่าวถึง "หมวดหมู่ของ endofunctors on Hask " ไม่ว่าในกรณีใดก็ตามใครก็ตามที่สงสัยว่าการที่ monad เป็น monoid ในประเภทของ endofunctors อาจได้ประโยชน์จากการอ่านที่มานี้
:-)
หรือเป็นเราด้วยความรักหมายถึงพวกเขาในเว็บไซต์นี้เป็นผู้ดูแล
ผมมาโพสต์นี้โดยวิธีการที่ดีกว่าการทำความเข้าใจการอนุมานของคำพูดที่น่าอับอายจากเครื่อง Mac เลนของทฤษฎีหมวดหมู่สำหรับนักคณิตศาสตร์ทำงาน
ในการอธิบายสิ่งที่เป็นสิ่งที่มักจะมีประโยชน์เท่าเทียมกันเพื่ออธิบายสิ่งที่ไม่
ข้อเท็จจริงที่ว่า Mac Lane ใช้คำอธิบายเพื่ออธิบาย Monad หนึ่งอาจบ่งบอกว่ามันอธิบายบางสิ่งบางอย่างที่ไม่ซ้ำกับ monads อดทนกับฉัน เพื่อพัฒนาความเข้าใจที่กว้างขึ้นเกี่ยวกับคำแถลงนี้ผมเชื่อว่ามันต้องชัดเจนว่าเขาไม่ได้อธิบายสิ่งที่เป็นเอกลักษณ์ของพระสงฆ์ ข้อความนี้อธิบายถึงการใช้งานและลูกศรอย่างเท่าเทียมกัน ด้วยเหตุผลเดียวกันเราสามารถมี monoids สองตัวบน Int (Sum และ Product) เราสามารถมี monoids หลายตัวบน X ในหมวดหมู่ของ endofunctors แต่มีความคล้ายคลึงกันมากขึ้น
ทั้ง Monad และ Applicative ตรงตามเกณฑ์:
(เช่นในแต่ละวันTree a -> List b
แต่ในหมวดหมู่Tree -> List
)
Tree -> List
List -> List
คำสั่งใช้ "หมวดหมู่ของ ... " ซึ่งจะกำหนดขอบเขตของคำสั่ง เป็นตัวอย่างที่ Functor หมวดหมู่อธิบายขอบเขตของf * -> g *
เช่นAny functor -> Any functor
เช่นหรือ Tree * -> List *
Tree * -> Tree *
สิ่งที่เป็นคำสั่งเด็ดขาดไม่ได้ระบุอธิบายที่ทุกอย่างจะได้รับอนุญาต
ในกรณีนี้ภายใน functors ที่* -> *
รู้จักกันในชื่อไม่ได้ระบุซึ่งหมายความว่าa -> b
Anything -> Anything including Anything else
ในฐานะที่เป็นจินตนาการของฉันกระโดดไป Int -> String ก็ยังมีInteger -> Maybe Int
หรือแม้กระทั่งที่ Maybe Double -> Either String Int
a :: Maybe Double; b :: Either String Int
ดังนั้นคำสั่งมารวมกันดังนี้:
:: f a -> g b
(เช่นชนิดใด ๆ ที่กำหนดพารามิเตอร์เป็นชนิดที่กำหนดพารามิเตอร์ใด ๆ ):: f a -> f b
(กล่าวคือประเภทใด ๆ ที่กำหนดพารามิเตอร์ชนิดที่ปรับพารามิเตอร์เดียวกัน) ... กล่าวว่าแตกต่างกันแล้วพลังของสิ่งก่อสร้างนี้อยู่ที่ไหน? เพื่อชื่นชมการเปลี่ยนแปลงเต็มรูปแบบฉันต้องเห็นว่าภาพวาดทั่วไปของ monoid (วัตถุเดียวที่มีลักษณะเหมือนลูกศรเอกลักษณ์:: single object -> single object
) ล้มเหลวในการแสดงให้เห็นว่าฉันได้รับอนุญาตให้ใช้ลูกศรที่กำหนดพารามิเตอร์ด้วยค่า monoid จำนวนเท่าใดก็ได้จากวัตถุประเภทเดียวที่ได้รับอนุญาตใน Monoid endo, ~ identity arrow definition ของความเท่าเทียมกันจะละเว้นค่าประเภทของ functor และทั้งประเภทและค่าของเลเยอร์ "payload" ที่อยู่ด้านในสุด ดังนั้นความเท่าเทียมจะส่งกลับtrue
ในทุกสถานการณ์ที่ประเภท functorial ตรงกัน (เช่นNothing -> Just * -> Nothing
เทียบเท่าJust * -> Just * -> Just *
เพราะทั้งคู่Maybe -> Maybe -> Maybe
)
แถบด้านข้าง: ~ นอกเป็นแนวความคิด f a
แต่ด้านซ้ายสัญลักษณ์มากที่สุดใน นอกจากนี้ยังอธิบายถึงสิ่งที่ "Haskell" อ่านในครั้งแรก (ภาพใหญ่); ดังนั้น Type คือ "ภายนอก" ที่สัมพันธ์กับ Value Type ความสัมพันธ์ระหว่างเลเยอร์ (สายการอ้างอิง) ในการเขียนโปรแกรมไม่ใช่เรื่องง่ายที่จะเกี่ยวข้องในหมวดหมู่ หมวดหมู่ชุดใช้เพื่ออธิบายประเภท (Int, Strings, บางที Int ฯลฯ ) ซึ่งรวมถึงหมวดหมู่ของ Functor (ประเภทพารามิเตอร์) ห่วงโซ่การอ้างอิง: ประเภทของ Functor, ค่าของ Functor (องค์ประกอบของชุดของ Functor นั้น, เช่น Nothing, Just) และในทางกลับกันทุกอย่างอื่นที่แต่ละค่าของ functor ชี้ไป ในหมวดหมู่ความสัมพันธ์นั้นถูกอธิบายแตกต่างกันเช่นreturn :: a -> m a
ถือเป็นการเปลี่ยนแปลงตามธรรมชาติจาก Functor หนึ่งไปยัง Functor อื่นซึ่งแตกต่างจากสิ่งที่กล่าวถึงในตอนนี้
กลับไปที่เธรดหลักทั้งหมดสำหรับผลิตภัณฑ์เทนเซอร์ที่กำหนดไว้ใด ๆ และค่าที่เป็นกลางคำแถลงนี้อธิบายถึงโครงสร้างการคำนวณที่ทรงพลังอย่างน่าอัศจรรย์ที่เกิดจากโครงสร้างที่ขัดแย้งกัน:
:: List
); คงที่fold
ที่ไม่ได้บอกอะไรเลยเกี่ยวกับเพย์โหลด)ใน Haskell การชี้แจงความเกี่ยวข้องของข้อความเป็นสิ่งสำคัญ อำนาจและความเก่งกาจของโครงสร้างนี้มีอะไรอย่างจะทำอย่างไรกับ monad ต่อ se กล่าวอีกนัยหนึ่งการสร้างไม่ได้ขึ้นอยู่กับสิ่งที่ทำให้ Monad มีความโดดเด่น
เมื่อพยายามที่จะคิดออกว่าจะสร้างรหัสด้วยบริบทที่ใช้ร่วมกันเพื่อสนับสนุนการคำนวณที่ต้องพึ่งพาซึ่งกันและกันหรือไม่กับการคำนวณที่สามารถทำงานแบบขนานได้ข้อความที่น่าอับอายนี้ การใช้งานลูกศรและ Monads แต่เป็นคำอธิบายของพวกเขาเท่ากัน สำหรับการตัดสินใจที่มีอยู่จริง
นี่มักจะเข้าใจผิด คำแถลงยังอธิบายต่อไปว่าjoin :: m (m a) -> m a
เป็นผลิตภัณฑ์เทนเซอร์สำหรับเอนโดโฟนตัวเดียว อย่างไรก็ตามมันไม่ได้อธิบายว่าในบริบทของคำแถลง(<*>)
นี้ได้รับการเลือกเช่นกันอย่างไร มันเป็นตัวอย่างของหก / ครึ่งโหลอย่างแท้จริง ตรรกะสำหรับการรวมค่านั้นเหมือนกันหมด อินพุตเดียวกันสร้างเอาต์พุตเดียวกันจากแต่ละรายการ (ซึ่งแตกต่างจากผลรวม Sum และ Product สำหรับ Int เพราะจะสร้างผลลัพธ์ที่แตกต่างเมื่อรวม Ints)
ดังนั้นเพื่อสรุป: monoid ในหมวดหมู่ของ endofunctors อธิบาย:
~t :: m * -> m * -> m *
and a neutral value for m *
(<*>)
และ(>>=)
ให้การเข้าถึงสองm
ค่าพร้อมกันเพื่อคำนวณค่าส่งคืนเดียว ตรรกะที่ใช้ในการคำนวณค่าส่งคืนเหมือนกันทุกประการ หากไม่ใช่สำหรับรูปร่างที่แตกต่างกันของฟังก์ชั่นที่พวกเขาแปร ( f :: a -> b
และk :: a -> m b
) และตำแหน่งของพารามิเตอร์ที่มีประเภทการคำนวณแบบส่งคืนเดียวกัน (เช่นa -> b -> b
เมื่อเทียบกับb -> a -> b
สำหรับแต่ละลำดับ) ฉันสงสัยว่าเราจะได้แปรพารามิเตอร์ตรรกะตรรกะ ผลิตภัณฑ์เทนเซอร์สำหรับนำมาใช้ใหม่ในทั้งสองคำจำกัดความ เป็นแบบฝึกหัดที่จะทำให้ประเด็นลองและนำไปใช้~t
และคุณจะจบลงด้วย(<*>)
และ(>>=)
ขึ้นอยู่กับว่าคุณตัดสินใจกำหนดมันforall a b
อย่างไร
หากจุดสุดท้ายของฉันเป็นอย่างน้อยที่สุดจริงแนวคิดมันก็จะอธิบายความแตกต่างที่แม่นยำและเฉพาะการคำนวณระหว่างการใช้งานและ Monad: ฟังก์ชั่นที่พวกเขาแปรปรวน ในคำอื่น ๆ คือความแตกต่างภายนอกในการดำเนินการของการเรียนประเภทเหล่านี้
โดยสรุปจากประสบการณ์ของฉันเองคำพูดที่น่าอับอายของ Mac Lane ได้ให้คำแนะนำ "goto" meme ที่ดีซึ่งเป็นป้ายบอกทางให้ฉันอ้างอิงขณะนำทางผ่านหมวดของฉันเพื่อทำความเข้าใจสำนวนที่ใช้ใน Haskell ให้ดีขึ้น มันประสบความสำเร็จในการบันทึกขอบเขตของความสามารถในการคำนวณที่มีประสิทธิภาพซึ่งทำให้สามารถเข้าถึงได้อย่างมหัศจรรย์ใน Haskell
อย่างไรก็ตามมีประชดในวิธีที่ฉันเข้าใจผิดครั้งแรกการบังคับใช้คำสั่งที่นอกเหนือจาก monad และสิ่งที่ฉันหวังว่าถ่ายทอดที่นี่ ทุกสิ่งที่อธิบายจะกลายเป็นสิ่งที่คล้ายกันระหว่าง Applicative และ Monads (และ Arrows ท่ามกลางคนอื่น ๆ ) สิ่งที่ไม่ได้กล่าวคือความแตกต่างเล็ก ๆ แต่มีประโยชน์อย่างแม่นยำ
- จ
คำตอบที่นี่ทำงานได้อย่างยอดเยี่ยมในการกำหนดทั้ง monoids และ monads อย่างไรก็ตามพวกเขายังดูเหมือนจะไม่ตอบคำถาม:
และในบันทึกย่อที่สำคัญน้อยกว่านี่คือความจริงและถ้าเป็นเช่นนั้นคุณสามารถให้คำอธิบายได้ (หวังว่าจะมีคนที่ไม่เข้าใจประสบการณ์ของ Haskell มาก)
ประเด็นสำคัญของสิ่งที่ขาดหายไปที่นี่คือแนวคิดที่แตกต่างกันของ "monoid" การจำแนกประเภทที่เรียกว่าแม่นยำยิ่งขึ้น - หนึ่งของ monoid ในหมวดหมู่ monoidal หนังสือของ Sadly Mac Lane นั้นทำให้เกิดความสับสน :
ทั้งหมดบอกว่า monad ใน
X
เป็นเพียง monoid ในหมวดหมู่ของ endofunctors ของX
ด้วยผลิตภัณฑ์ที่×
ถูกแทนที่ด้วยองค์ประกอบของ endofunctors และหน่วยที่กำหนดโดย endofunctor ตัวตน
ทำไมถึงสับสน? เพราะมันไม่ได้กำหนดว่าอะไรคือ "หนังสือในหมวดหมู่ของ endofunctors ว่า" X
ของ แต่ประโยคนี้แสดงให้เห็นว่ามีการสร้าง monoid ภายในชุดของ endofunctors ทั้งหมดพร้อมกับการจัดองค์ประกอบของ functor เป็นการดำเนินการแบบไบนารี่ ซึ่งทำงานได้ดีอย่างสมบูรณ์แบบและเปลี่ยนเป็น monoid ส่วนย่อยของ endofunctors ใด ๆ ที่มี functor ตัวตนและถูกปิดภายใต้องค์ประกอบ functor
แต่นี่ไม่ใช่การตีความที่ถูกต้องซึ่งหนังสือไม่สามารถอธิบายได้อย่างชัดเจนในขั้นตอนนั้น Monad f
เป็นendofunctor คงที่ไม่ใช่เซตย่อยของ endofunctors ที่ปิดภายใต้การจัดองค์ประกอบ การสร้างร่วมกันคือการใช้f
เพื่อสร้างหนังสือโดยการตั้งค่าของทุกk
องค์ประกอบเท่าf^k = f(f(...))
ของf
ด้วยตัวเองรวมทั้งสอดคล้องกับตัวตนที่k=0
f^0 = id
และตอนนี้ชุดS
ของพลังทั้งหมดเหล่านี้สำหรับทุกคนk>=0
ย่อมเป็น monoid "กับผลิตภัณฑ์×แทนที่ด้วยองค์ประกอบของ endofunctors และหน่วยที่กำหนดโดย endofunctor เอกลักษณ์"
และยัง:
S
สามารถกำหนดสำหรับ functor ใด ๆf
หรือแม้กระทั่งตัวอักษรสำหรับการใด ๆ X
ด้วยตนเองแผนที่ของ f
มันเป็นหนังสือที่สร้างขึ้นโดยS
กำหนดโดยองค์ประกอบ functor และ functor เอกลักษณ์ไม่มีอะไรเกี่ยวข้องกับf
การเป็นหรือไม่เป็น monadและเพื่อให้สิ่งที่มากขึ้นทำให้เกิดความสับสนความหมายของ "หนังสือในหมวดหมู่ monoidal" มาต่อมาในหนังสือที่คุณสามารถดูจากตารางของเนื้อหา และยังเข้าใจความคิดนี้เป็นสิ่งสำคัญอย่างยิ่งที่จะเข้าใจการเชื่อมต่อกับพระ
ไปบทที่เจ็ดใน Monoids (ที่มาช้ากว่าบทที่หกบน Monads) เราจะพบความหมายของสิ่งที่เรียกว่าหมวดหมู่ monoidal เข้มงวดเป็นสาม(B, *, e)
ที่B
เป็นหมวดหมู่, bifunctor (functor ที่เกี่ยวกับส่วนประกอบแต่ละคนมีองค์ประกอบอื่น ๆ คงที่ ) และเป็นวัตถุหน่วยในการตอบสนองความสัมพันธ์และกฎหมายหน่วย:*: B x B-> B
e
B
(a * b) * c = a * (b * c)
a * e = e * a = a
สำหรับวัตถุใด ๆa,b,c
ของB
และอัตลักษณ์เดียวกัน morphisms ใด ๆa,b,c
กับการe
แทนที่ด้วยการซึ่มส์ตัวตนของid_e
e
ขณะนี้เป็นคำแนะนำให้สังเกตว่าในกรณีที่เราสนใจซึ่งB
เป็นประเภทของ endofunctors ของX
กับการเปลี่ยนแปลงตามธรรมชาติเป็น morphisms *
องค์ประกอบ functor และe
functor ตัวตนกฎหมายเหล่านี้มีความพึงพอใจทั้งหมดที่สามารถตรวจสอบได้โดยตรง
สิ่งที่เกิดขึ้นภายหลังในหนังสือเล่มนี้คือคำจำกัดความของหมวดหมู่ "ผ่อนคลาย" ซึ่งมีเพียงโมดูโล่ที่มีการเปลี่ยนแปลงตามธรรมชาติบางอย่างที่น่าพอใจซึ่งเรียกว่าความสัมพันธ์ที่เชื่อมโยงกันซึ่งไม่สำคัญสำหรับกรณีของเราในหมวดหมู่ endofunctor
สุดท้ายในส่วนที่ 3 "Monoids" ของบทที่ VII คำจำกัดความที่แท้จริงได้รับ:
monoid
c
ในประเภท monoidal(B, *, e)
เป็นวัตถุที่B
มีลูกศรสองอัน (morphisms)
mu: c * c -> c
nu: e -> c
ทำให้ 3 แผนภาพสับเปลี่ยน จำได้ว่าในกรณีของเราเหล่านี้เป็น morphisms ในหมวดหมู่ของ endofunctors ซึ่งเป็นการเปลี่ยนแปลงตามธรรมชาติที่สอดคล้องกับแม่นยำjoin
และreturn
สำหรับ monad การเชื่อมต่อจะชัดเจนยิ่งขึ้นเมื่อเราทำให้การจัดองค์ประกอบ*
ชัดเจนมากขึ้นแทนที่c * c
ด้วยmonad ของเราอยู่c^2
ที่ไหนc
ในที่สุดสังเกตว่า 3 ไดอะแกรมสลับสับเปลี่ยน (ในคำจำกัดความของ monoid ในหมวดหมู่ monoidal) เขียนสำหรับหมวดหมู่ monoidal ทั่วไป (ไม่เข้มงวด) ในขณะที่ในกรณีของเราการเปลี่ยนแปลงตามธรรมชาติทั้งหมดที่เกิดขึ้นเป็นส่วนหนึ่งของหมวดหมู่ monoidal นั่นจะทำให้ไดอะแกรมเหมือนกับที่อยู่ในคำจำกัดความของ monad ทำให้การติดต่อทางจดหมายเสร็จสมบูรณ์
ในการสรุป monad ใด ๆ ที่เป็นโดยความหมาย endofunctor ดังนั้นวัตถุที่อยู่ในหมวดหมู่ของ endofunctors ที่ที่เอกjoin
และreturn
ผู้ประกอบการตอบสนองความหมายของหนังสือในเฉพาะ (เข้มงวด) หมวด ในทางกลับกัน monoid ใด ๆ ในหมวดหมู่ monoidal ของ endofunctors โดยนิยามสาม(c, mu, nu)
ประกอบด้วยวัตถุและลูกศรสองลูกเช่นการเปลี่ยนแปลงตามธรรมชาติในกรณีของเราพอใจกฎหมายเดียวกันกับ monad
ในที่สุดให้สังเกตความแตกต่างที่สำคัญระหว่าง monoids (แบบคลาสสิก) และ monoids ทั่วไปในหมวดหมู่ monoidal ทั้งสองลูกศรmu
และnu
ข้างต้นไม่ได้อีกต่อไปดำเนินการทวิภาคและหน่วยในชุด แต่คุณมีหนึ่ง c
endofunctor องค์ประกอบนักแต่งเพลง*
และนักแสดงตัวตนเพียงอย่างเดียวไม่ได้จัดเตรียมโครงสร้างที่สมบูรณ์ที่จำเป็นสำหรับ monad แม้จะมีคำพูดที่สับสนในหนังสือ
อีกวิธีหนึ่งที่จะเปรียบเทียบกับหนังสือมาตรฐานC
ของทุกแผนที่ตนเองของชุดA
ที่ดำเนินการทวิภาคเป็นองค์ประกอบที่สามารถมองเห็นไปยังแผนที่ผลิตภัณฑ์คาร์ทีเซียนมาตรฐานเข้าไปC x C
C
ผ่านไปยังหมวดหมู่ monoid เรากำลังเปลี่ยนผลิตภัณฑ์ cartesian x
ด้วยองค์ประกอบ functor *
และการดำเนินการแบบไบนารีจะถูกแทนที่ด้วยการแปลงตามธรรมชาติmu
จาก
c * c
ไปc
เป็นนั่นคือชุดของjoin
ผู้ประกอบการ
join: c(c(T))->c(T)
สำหรับวัตถุทุกT
ชนิด (พิมพ์โปรแกรม) และองค์ประกอบประจำตัวใน monoids แบบคลาสสิกซึ่งสามารถระบุด้วยภาพของแผนที่จากจุดหนึ่งจุดคงที่ได้รับการแทนที่ด้วยคอลเลกชันของreturn
ผู้ประกอบการ
return: T->c(T)
แต่ตอนนี้ไม่มีผลิตภัณฑ์คาร์ทีเซียนอีกต่อไปดังนั้นจึงไม่มีคู่ขององค์ประกอบและจึงไม่มีการดำเนินการแบบไบนารี