ใบสมัครเขียน monads ไม่ได้


110

ใบสมัครเขียน monads ไม่ได้

ข้อความข้างต้นหมายถึงอะไร? และเมื่อใดที่เป็นที่นิยมมากกว่าคนอื่น ๆ ?


5
คุณได้คำพูดนี้มาจากไหน? การดูบริบทบางอย่างอาจเป็นประโยชน์
fuz

@FUZxxl: ฉันได้ยินมันซ้ำ ๆ จากหลาย ๆ คนเมื่อเร็ว ๆ นี้จาก debasishg บน Twitter
missingfaktor

3
Tetley @stephen: โปรดทราบว่าหลายอย่างเช่นApplicatives เป็นจริงทั้งครอบครัวของMonads คือหนึ่งสำหรับแต่ละ "รูปร่าง" ของโครงสร้างที่เป็นไปได้ ZipListไม่ใช่ a Monadแต่ZipListมีความยาวคงที่คือ Readerเป็นกรณีพิเศษที่สะดวก (หรือโดยทั่วไป?) ที่ขนาดของ "โครงสร้าง" ได้รับการแก้ไขเป็นจำนวนเต็มของประเภทสภาพแวดล้อม
CA McCann

3
@CAMcCann แอปพลิเคชัน zippy ทั้งหมด (ไม่ว่าจะตัดทอนหรือแผ่นรอง) จำกัด เฉพาะ monads หากคุณแก้ไขรูปร่างในลักษณะที่มีปริมาณReadermonad ถึง isomorphism เมื่อคุณแก้ไขรูปร่างของคอนเทนเนอร์แล้วจะเข้ารหัสฟังก์ชันจากตำแหน่งได้อย่างมีประสิทธิภาพเช่นสามบันทึก ปีเตอร์แฮนค็อกเรียกสัตว์ประเภทนี้ว่า "เนเพอเรียน" เนื่องจากพวกเขาปฏิบัติตามกฎของลอการิทึม
pigworker

4
@stephen tetley: ตัวอย่างอื่น ๆ ได้แก่ แอปพลิเคชันแบบโมโนนอยด์คงที่ (ซึ่งเป็นองค์ประกอบของ monads แต่ไม่ใช่ monad) และแอปพลิเคชันการหน่วงเวลาหน่วย (ซึ่งดีกว่าไม่ยอมรับการเข้าร่วม)
pigworker

คำตอบ:


115

หากเราเปรียบเทียบประเภทต่างๆ

(<*>) :: Applicative a => a (s -> t) -> a s -> a t
(>>=) :: Monad m =>       m s -> (s -> m t) -> m t

เราได้เบาะแสว่าอะไรที่แยกแนวคิดทั้งสองออกจากกัน ว่า(s -> m t)ในรูปแบบของการ(>>=)แสดงให้เห็นว่าค่าในสามารถตรวจสอบพฤติกรรมของการคำนวณในs m tMonads อนุญาตให้มีการรบกวนระหว่างชั้นค่าและการคำนวณ ตัว(<*>)ดำเนินการไม่อนุญาตให้มีการรบกวนดังกล่าว: ฟังก์ชันและการคำนวณอาร์กิวเมนต์ไม่ได้ขึ้นอยู่กับค่า นี่มันกัดจริงๆ เปรียบเทียบ

miffy :: Monad m => m Bool -> m x -> m x -> m x
miffy mb mt mf = do
  b <- mb
  if b then mt else mf

ซึ่งใช้ผลลัพธ์ของเอฟเฟกต์บางอย่างในการตัดสินใจระหว่างการคำนวณสองครั้ง(เช่นการยิงขีปนาวุธและการลงนามในการสงบศึก) ในขณะที่

iffy :: Applicative a => a Bool -> a x -> a x -> a x
iffy ab at af = pure cond <*> ab <*> at <*> af where
  cond b t f = if b then t else f

ซึ่งใช้ค่าของabการเลือกระหว่างค่าของการคำนวณสองค่าatและafการดำเนินการทั้งสองอย่างอาจส่งผลที่น่าเศร้า

เวอร์ชัน monadic นั้นอาศัยพลังพิเศษใน(>>=)การเลือกการคำนวณจากค่าเป็นหลักและนั่นอาจมีความสำคัญ อย่างไรก็ตามการสนับสนุนพลังดังกล่าวทำให้ monads ยากที่จะแต่ง ถ้าเราพยายามสร้าง 'ผูกสองครั้ง'

(>>>>==) :: (Monad m, Monad n) => m (n s) -> (s -> m (n t)) -> m (n t)
mns >>>>== f = mns >>-{-m-} \ ns -> let nmnt = ns >>= (return . f) in ???

เรามาไกลขนาดนี้ แต่ตอนนี้เลเยอร์ของเราทั้งหมดพังทลาย เรามีดังนั้นเราจึงจำเป็นที่จะกำจัดของนอกn (m (n t)) nดังที่ Alexandre C กล่าวว่าเราสามารถทำได้ถ้าเรามีความเหมาะสม

swap :: n (m t) -> m (n t)

เพื่อเปลี่ยนรูปnเข้ามาและก็ไปที่อื่น ๆjoinn

'ใช้สองครั้ง' ที่อ่อนแอกว่าจะกำหนดได้ง่ายกว่ามาก

(<<**>>) :: (Applicative a, Applicative b) => a (b (s -> t)) -> a (b s) -> a (b t)
abf <<**>> abs = pure (<*>) <*> abf <*> abs

เนื่องจากไม่มีการรบกวนระหว่างเลเยอร์

ในทำนองเดียวกันมันเป็นการดีที่จะรับรู้เมื่อคุณต้องการพลังพิเศษของMonads และเมื่อไหร่ที่คุณสามารถหลีกหนีจากโครงสร้างการคำนวณที่แข็งแกร่งที่Applicativeรองรับได้

โปรดทราบว่าแม้ว่าการแต่งเพลงจะยาก แต่ก็อาจมากกว่าที่คุณต้องการ ประเภทm (n v)ระบุการคำนวณด้วยm-effects จากนั้นคำนวณด้วยn-effects ถึง a -value vโดยที่m-effects จะเสร็จสิ้นก่อนที่n-effects จะเริ่มต้น (ดังนั้นจึงจำเป็นสำหรับswap) หากคุณเพียงแค่ต้องการแทรก - เอฟmเฟกต์กับเอฟnเฟกต์องค์ประกอบก็อาจจะมากเกินไปที่จะถาม!


3
สำหรับตัวอย่าง iffy คุณระบุว่า "ใช้ค่าของ ab เพื่อเลือกระหว่างค่าของการคำนวณสองค่าที่และ af โดยดำเนินการทั้งสองอย่างแล้วอาจส่งผลที่น่าเศร้า" นิสัยขี้เกียจของ Haskell ปกป้องคุณจากสิ่งนี้ไม่ใช่หรือ? ถ้าฉันมี list = (\ btf -> ถ้า b แล้ว t else f): [] แล้วรันคำสั่ง: list <*> pure True <*> pure "hello" <*> pure (error "bad") ... ฉันได้รับ "สวัสดี" และข้อผิดพลาดไม่เคยเกิดขึ้น รหัสนี้ไม่ได้เกือบจะปลอดภัยหรือควบคุมได้เหมือนกับ monad แต่โพสต์ดูเหมือนว่าจะแนะนำว่าแอปพลิเคชันทำให้เกิดการประเมินที่เข้มงวด โพสต์ที่ดีโดยรวมแม้ว่า! ขอบคุณ!
shj

7
คุณยังคงได้รับผลกระทบของทั้งสองอย่าง แต่บริสุทธิ์ (ข้อผิดพลาด "ไม่ดี") ไม่มีเลย ในทางกลับกันหากคุณลองใช้ iffy (pure True) ("สวัสดี") (ข้อผิดพลาด "ไม่ดี") คุณจะได้รับข้อผิดพลาดที่หลีกเลี่ยงไม่ได้ ยิ่งไปกว่านั้นถ้าคุณลองบางอย่างเช่น iffy (pure True) (pure 0) [1,2] คุณจะได้รับ [0,0] แทนที่จะเป็น [0] แอปพลิเคชันมีความเข้มงวดเกี่ยวกับพวกเขาเนื่องจากพวกเขาสร้างลำดับการคำนวณคงที่ แต่ค่าที่เกิดจากการคำนวณเหล่านั้นยังคงรวมกันอย่างเฉื่อยชาตามที่คุณสังเกต
pigworker

มันเป็นความจริงว่าสำหรับ monads ใด ๆmและnคุณก็สามารถเขียนหม้อแปลง monad mtและการดำเนินงานในn (m t)การใช้mt n t? ดังนั้นคุณสามารถเขียน monads ได้ตลอดเวลามันซับซ้อนกว่านี้โดยใช้หม้อแปลง?
ron

4
หม้อแปลงดังกล่าวมักจะมีอยู่ แต่เท่าที่ฉันรู้ไม่มีวิธีที่เป็นที่ยอมรับในการสร้างมัน มักจะมีทางเลือกที่แท้จริงเกี่ยวกับวิธีแก้ไขเอฟเฟกต์แบบสอดแทรกจาก monads ต่างๆตัวอย่างคลาสสิกคือข้อยกเว้นและสถานะ ข้อยกเว้นควรย้อนกลับสถานะเปลี่ยนหรือไม่? ทางเลือกทั้งสองมีสถานที่ของพวกเขา ต้องบอกว่ามี "โมนาดฟรี" ที่แสดงออกถึง "การสอดแทรกโดยพลการ" data Free f x = Ret x | Do (f (Free f x))แล้วและพิจารณาdata (:+:) f g x = Inl (f x) | Tnr (g x) Free (m :+: n)ซึ่งจะทำให้ตัวเลือกในการเรียกใช้ interleavings ล่าช้า
pigworker

@pigworker เกี่ยวกับการถกเถียงขี้เกียจ / เข้มงวด ฉันคิดว่าด้วยแอปพลิเคชันคุณไม่สามารถควบคุมเอฟเฟกต์จากภายในการคำนวณได้ แต่เอฟเฟกต์เลเยอร์สามารถตัดสินใจที่จะไม่ประเมินค่าในภายหลังได้เป็นอย่างดี สำหรับตัวแยกวิเคราะห์ (แอปพลิเคชัน) หมายความว่าหากตัวแยกวิเคราะห์ล้มเหลวในช่วงต้นตัวแยกวิเคราะห์ที่ตามมาจะไม่ได้รับการประเมิน / นำไปใช้กับอินพุต สำหรับMaybeวิธีการนี้ว่าในช่วงต้นNothingจะปราบปรามการประเมินผลของการต่อมาa / ที่ตามมา Just aถูกต้องหรือไม่
ziggystar

75

ใบสมัครเขียน monads ไม่ได้

Monads ทำเขียน แต่ผลที่ได้อาจจะไม่ monad ในทางตรงกันข้ามองค์ประกอบของแอปพลิเคชันสองรายการจำเป็นต้องมีการประยุกต์ใช้ ฉันสงสัยว่าเจตนาของข้อความเดิมคือ "การบังคับใช้ประกอบด้วยในขณะที่ความน่าเชื่อถือไม่ได้" Rephrased " Applicativeถูกปิดภายใต้การเรียบเรียงและMonadไม่ใช่"


24
นอกจากนี้แอปพลิเคชันสองรายการใด ๆ ยังประกอบขึ้นด้วยวิธีเชิงกลอย่างสมบูรณ์ในขณะที่ monad ที่เกิดจากองค์ประกอบของ monads สองตัวนั้นมีความเฉพาะเจาะจงกับองค์ประกอบนั้น ๆ
Apocalisp

12
ยิ่งไปกว่านั้น monads ยังประกอบในรูปแบบอื่น ๆ ผลิตภัณฑ์ของ monad สองตัวคือ monad มันเป็นเพียงผลิตภัณฑ์ร่วมที่ต้องการกฎการกระจายบางประเภทเท่านั้น
Edward KMETT

ด้วย @Apocalisp รวมความคิดเห็นนี่คือคำตอบที่ดีที่สุดและกระชับที่สุด
Paul Draper

39

หากคุณมีแอปพลิเคชันA1และA2ประเภทdata A3 a = A3 (A1 (A2 a))นั้นก็ใช้บังคับได้เช่นกัน (คุณสามารถเขียนอินสแตนซ์ดังกล่าวด้วยวิธีทั่วไป)

ในทางตรงกันข้ามถ้าคุณมี monads M1และM2แล้วชนิดdata M3 a = M3 (M1 (M2 a))ไม่จำเป็นต้อง monad (มีคือไม่มีการใช้งานทั่วไปที่เหมาะสมสำหรับ>>=หรือjoinสำหรับองค์ประกอบ)

ตัวอย่างหนึ่งอาจเป็นประเภท[Int -> a](ที่นี่เราเขียนตัวสร้างประเภท[]ด้วย(->) Intซึ่งทั้งสองอย่างนี้เป็น monads) คุณสามารถเขียนได้อย่างง่ายดาย

app :: [Int -> (a -> b)] -> [Int -> a] -> [Int -> b]
app f x = (<*>) <$> f <*> x

และที่กล่าวโดยทั่วไปสำหรับการใช้งานใด ๆ :

app :: (Applicative f, Applicative f1) => f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)

แต่ไม่มีคำจำกัดความที่สมเหตุสมผลของ

join :: [Int -> [Int -> a]] -> [Int -> a]

หากคุณไม่มั่นใจในสิ่งนี้ให้พิจารณานิพจน์นี้:

join [\x -> replicate x (const ())]

ต้องตั้งค่าความยาวของรายการที่ส่งคืนเป็นจำนวนเต็มก่อนที่จะมีการระบุจำนวนเต็ม แต่ความยาวที่ถูกต้องขึ้นอยู่กับจำนวนเต็มที่ระบุ ดังนั้นจึงไม่มีjoinฟังก์ชันที่ถูกต้องสำหรับประเภทนี้


1
... ดังนั้นหลีกเลี่ยง monads เมื่อฟังก์ชั่นจะทำอย่างไร
andrew cooke

2
@andrew ถ้าคุณหมายถึง functor ใช่แล้ว functors นั้นง่ายกว่าและควรใช้เมื่อเพียงพอ โปรดทราบว่ามันไม่เสมอไป ตัวอย่างเช่นหากIOไม่มีโปรแกรมMonadจะยากมาก :)
รอทส.

17

น่าเสียดายที่เป้าหมายที่แท้จริงของเราซึ่งเป็นองค์ประกอบของ monads นั้นค่อนข้างยากกว่า .. ในความเป็นจริงเราสามารถพิสูจน์ได้ว่าในแง่หนึ่งไม่มีวิธีใดที่จะสร้างฟังก์ชันการเข้าร่วมกับประเภทข้างต้นโดยใช้เฉพาะการดำเนินการของสอง monads (ดูภาคผนวกสำหรับโครงร่างของการพิสูจน์) เป็นไปตามนั้นวิธีเดียวที่เราอาจหวังว่าจะสร้างองค์ประกอบคือหากมีสิ่งก่อสร้างเพิ่มเติมที่เชื่อมโยงองค์ประกอบทั้งสองเข้าด้วยกัน

การเขียน monads http://web.cecs.pdx.edu/~mpj/pubs/RR-1004.pdf


4
Tl; dr สำหรับผู้อ่านที่ใจร้อน: คุณสามารถเขียน monads ได้ถ้า (f?) คุณสามารถสร้างการเปลี่ยนแปลงตามธรรมชาติได้swap : N M a -> M N a
Alexandre C.

@Alexandre C .: แค่ "ถ้า" ฉันสงสัย หม้อแปลง monad บางตัวไม่ได้อธิบายโดยองค์ประกอบของ functor โดยตรง ยกตัวอย่างเช่นContT r m aเป็นค่าm (Cont r a)มิได้Cont r (m a)และเป็นประมาณStateT s m a Reader s (m (Writer s a))
CA McCann

@CA McCann: ฉันดูเหมือนจะไม่ได้รับจาก (M monad, N monad, MN monad, NM monad) ไปที่ (มีการแลกเปลี่ยนอยู่: MN -> NM natural) ตอนนี้เรามาดู "ถ้า" กันเถอะ (บางทีคำตอบอยู่ในกระดาษฉันต้องสารภาพว่าฉันค้นหามันอย่างรวดเร็ว)
อาเล็กซี

1
@ อเล็กซานเดอร์ซี: แค่ระบุว่าการแต่งเพลงเป็นแบบ monads อาจจะไม่เพียงพอต่อไป - คุณต้องมีวิธีเชื่อมโยงทั้งสองส่วนเข้ากับทั้งหมด การมีอยู่โดยswapนัยว่าองค์ประกอบทำให้ทั้งสอง "ร่วมมือกัน" อย่างใดอย่างหนึ่ง นอกจากนี้โปรดทราบว่าsequenceเป็นกรณีพิเศษของ "swap" สำหรับ monads บางตัว เป็นflipเช่นนั้นจริง
CA McCann

7
ในการเขียนswap :: N (M x) -> M (N x)ให้ฉันดูเหมือนว่าคุณสามารถใช้returns( fmapPed ที่เหมาะสม) เพื่อแทรกMที่ด้านหน้าและNด้านN (M x) -> M (N (M (N x)))หลังจากนั้นใช้joinคอมโพสิตเพื่อรับM (N x)ไฟล์.
pigworker

7

โซลูชันกฎหมายการกระจาย l: MN -> NM ก็เพียงพอแล้ว

เพื่อรับประกันความน่าเชื่อถือของ NM ในการดูสิ่งนี้คุณต้องมีหน่วยและหลายตัว ฉันจะเน้นที่ mult (หน่วยคือ unit_N unitM)

NMNM - l -> NNMM - mult_N mult_M -> NM

สิ่งนี้ไม่ได้รับประกันว่า MN เป็น monad

อย่างไรก็ตามการสังเกตที่สำคัญเกิดขึ้นเมื่อคุณมีโซลูชันกฎหมายแบบกระจาย

l1 : ML -> LM
l2 : NL -> LN
l3 : NM -> MN

ดังนั้น LM, LN และ MN จึงเป็น monads คำถามเกิดขึ้นว่า LMN เป็น monad หรือไม่ (โดย

(MN) L -> L (MN) หรือโดย N (LM) -> (LM) N

เรามีโครงสร้างเพียงพอที่จะสร้างแผนที่เหล่านี้ อย่างไรก็ตามตามที่Eugenia Cheng สังเกตเราจำเป็นต้องมีเงื่อนไขหกเหลี่ยม (ซึ่งเท่ากับการนำเสนอสมการ Yang-Baxter) เพื่อรับประกันความน่าเชื่อถือของการก่อสร้างอย่างใดอย่างหนึ่ง ในความเป็นจริงด้วยสภาพหกเหลี่ยม monads ทั้งสองจึงเกิดขึ้นพร้อมกัน


9
นี้น่าจะเป็นคำตอบที่ดี แต่มันก็หวือทางเหนือหัวของฉัน
Dan Burton

1
นั่นเป็นเพราะการใช้คำว่า Applicative และแท็ก haskell นี่เป็นคำถามเกี่ยวกับ haskell แต่มีคำตอบในรูปแบบอื่น
codehot
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.