Monads แก้ปัญหาการเขียนโปรแกรมอะไรบ้าง? [ปิด]


14

ฉันได้อ่านบทความจำนวนมากที่อธิบายว่าพระสงฆ์คืออะไรอย่างไรunitและbindทำงานอย่างไรบ้างบางคนก็ถกเถียงกันในทฤษฎีหมวดหมู่อย่างเป็นนามธรรมดังนั้น (อย่างน้อยสำหรับฉัน) ที่ทำให้ตาตกบางคนไม่สนใจสิ่งเหล่านั้น burritos กล่องและสิ่งที่ไม่

หลังจากสองสามสัปดาห์ของการศึกษาและเซลล์ประสาททอดจำนวนมาก (ฉันคิดว่า) ฉันเข้าใจว่า Monads ทำงานอย่างไร แต่ยังมีอีกสิ่งหนึ่งที่หนีความเข้าใจของฉันไปสิ่งที่โพสต์ไม่กี่สัมผัสจริง ๆ (ยกเว้น IO และรัฐ):

ทำไม?

ทำไม Monads ถึงมีความสำคัญ? ทำไมพวกเขาถึงสำคัญ? ปัญหาที่พวกเขากำลังแก้ไขคืออะไร ปัญหาเหล่านั้นสามารถแก้ไขได้เฉพาะกับ Monads หรือมีวิธีอื่นบ้างไหม?


3
โปรแกรมมีขนาดใหญ่และซับซ้อน เราต้องการวิธีในการจัดโครงสร้างพวกมัน มีหลายวิธี Monads เป็นหนึ่งเดียว ลูกศรเป็นหนึ่ง คนทำงานเป็นคนหนึ่ง วัตถุ, คลาส, ฟังก์ชั่น, วิธีการ, โมดูล, ลักษณะ, มิกซ์อิน, แพ็คเกจเป็นของคนอื่น ๆ , มันไม่ชัดเจนว่าคำถามเฉพาะของคุณคืออะไร คุณถามว่าทำไมเราต้องจัดโครงสร้างโปรแกรม?
Jörg W Mittag

1
@ JörgWMittag: ฉันไม่ได้ถามว่าทำไมเราต้องจัดโครงสร้างโปรแกรม เห็นได้ชัดว่าปัญหาใหญ่ถูกแบ่งออกเป็นปัญหาเล็ก ๆ ที่คุณสามารถแก้ไขแล้วรวมเข้าด้วยกันเพื่อแก้ปัญหาสำหรับปัญหาใหญ่ ฉันกำลังถามว่าปัญหาอะไรที่ Monads แก้ไข มันคืออะไร รหัสโครงสร้าง? เอะอะทั้งหมดนั้นเกี่ยวกับหรือไม่ ตัวอย่างเช่นใน Haskell นี่คือวิธีที่คุณทำ I / O ที่นั่น monad เป็นบทละครที่แกล้งทำตัวเหมือนเป็นอะไรที่บริสุทธิ์เมื่อในความเป็นจริงไม่ใช่ คำถามของฉันอยู่ในชื่อเรื่องฉันไม่ทราบวิธีระบุอย่างชัดเจน
Dummy Me



1
@RobertHarvey: ฉันได้พบลิงก์สองรายการแรกที่คุณเพิ่ม แต่เช่นเดียวกับในคำตอบของโพสต์เหล่านั้นและโพสต์อื่น ๆ คำตอบจะตรงไปที่บางที, รัฐและ IO monads, กระโดดไปที่ตัวอย่างหรือรหัสอย่างรวดเร็วและจบด้วย "มีจำนวนมาก ปัญหาที่สามารถแก้ไขได้โดยใช้ Monads " ปัญหาประเภทใดบ้าง ฉันกำลังมองหาคำตอบในระดับที่สูงขึ้นแทนที่จะพูดซ้ำตัวอย่างจริง ๆ แล้วไปที่รากของปัญหา (ไม่ว่าอะไรก็ตาม) และอธิบายว่า Monads แก้ปัญหาได้อย่างไรและทำไมจึงเป็นตัวเลือกที่ดีที่สุดเมื่อเทียบกับการใช้อย่างอื่น ขอบคุณมากสำหรับความคิดเห็นแม้ว่า
Dummy Me

คำตอบ:


10

คุณไม่ต้องการพระให้แก้ปัญหาอะไร พวกเขาทำให้บางสิ่งง่ายขึ้น ผู้คนจำนวนมากไปทางนามธรรมและทฤษฎีเกินไปเมื่ออธิบาย monads ส่วนใหญ่ monads เป็นรูปแบบที่เกิดขึ้นซ้ำแล้วซ้ำอีกในการเขียนโปรแกรม ด้วยการตระหนักถึงรูปแบบนั้นเราสามารถทำให้โค้ดของเราง่ายขึ้นและหลีกเลี่ยงการนำฟังก์ชั่นบางอย่างมาใช้ใหม่

สำหรับ Haskell จากจุดที่เป็นรูปธรรมของมุมมองมากที่สุดสิ่งที่มองเห็นได้ monads ช่วยให้มีที่ทำสัญกรณ์ รายการความเข้าใจใน Haskell และภาษาอื่น ๆ ก็ใช้ประโยชน์จาก monads เช่นกัน นอกจากนี้คุณยังสามารถสร้างห้องสมุดเช่นControl.Monad

เหล่านี้ทั้งหมดให้ simplifications ประโยชน์และเมื่อคุณได้ดำเนินการมันสำหรับหนึ่ง monad คุณจะได้รับมันทั้งหมด monads นี่คือหนึ่งในเหตุผลสำคัญว่าทำไมการใช้รหัสซ้ำง่ายกว่าสำหรับการเขียนโปรแกรมการทำงานมากกว่ากระบวนทัศน์อื่น ๆ


2
ฉันพูดได้ว่าสิ่งที่มองเห็นได้มากที่สุดคือการเปิดใช้งาน monkell เป็นวิธีที่ง่ายที่สุดในการใช้เอฟเฟกต์ IO ที่ใช้งานได้ตามที่คุณคาดหวัง ผมคิดว่าทุกคนสงสัยว่าสิ่งที่ monads ให้เราควรลองมิแรนดา มิแรนดาเป็นภาษา Haskell ได้รับการออกแบบมาเพื่อแทนที่และควรเป็นเรื่องง่ายมากที่จะเรียนรู้สำหรับทุกคนที่มีประสบการณ์ Haskell ความแตกต่างที่สำคัญคือมันไม่มี monadic IO ซึ่งทำให้ภาษาทำงานได้ยากกว่า Haskell สำหรับโครงการที่ไม่สำคัญ
จูลส์

ใช่IOเป็นmonad ที่โดดเด่นที่สุดของ Haskell แต่ฉันไม่ได้แสดงรายการตัวอย่างของ monads แต่ละรายการ ฉันกำลังแสดงรายการตัวอย่างของ abstractions ที่ครอบคลุมที่เปิดใช้งาน ฉันพยายามเข้าใจสาเหตุว่าทำไมผู้ชายคนแรกที่คิดว่าใช้ monad สำหรับ IO จะคิดว่าเป็นความคิดที่ดีโดยทั่วไป
Karl Bielefeldt

1
@Jules: หรือดูประวัติของ Haskell ก่อน Monads นักออกแบบ Haskell ได้ทดลองกับ Lazy Streams and Continuations เพื่อเป็นพื้นฐานสำหรับ I / O และทั้งคู่ก็ใช้ยาก
Jörg W Mittag

5

เป็นการง่ายกว่าที่จะเข้าใจถ้าคุณดูพระโดยเฉพาะและดูว่าพวกเขาแก้ปัญหาอย่างไร ตัวอย่างเช่นใน Haskell:

  • IO: อนุญาตให้ IO แสดงในระบบประเภทเพื่อให้คุณสามารถแยกฟังก์ชันบริสุทธิ์แบบแยกจากฟังก์ชั่นที่ใช้ IO

  • รายการ: อนุญาตให้คุณทำรายการความเข้าใจและการคำนวณแบบโหนด

  • อาจจะเป็น: ทางเลือกที่ดีกว่าสำหรับค่า null และสนับสนุนสิ่งที่เทียบเคียงได้กับโอเปอเรเตอร์การรวมศูนย์ของ C #

  • Parsec: DSL ที่สะดวกสำหรับการเขียนโปรแกรมแยกวิเคราะห์

ดังนั้นจึงเป็นเรื่องง่าย (ฉันหวังว่า) เพื่อดูเหตุผลของแต่ละพระเพราะพวกเขามีประโยชน์มาก แต่ปัญหาที่พวกเขาแก้ปัญหานั้นก็ค่อนข้างแตกต่างกันและพวกเขาดูเหมือนจะไม่ได้มีอะไรที่เหมือนกันมากนักยกเว้นพวกเขาทั้งหมดจะเกี่ยวข้องกับตรรกะบางอย่างสำหรับปฏิบัติการผูกมัด Monads นั้นมีประโยชน์เพราะมันช่วยให้คุณสร้างเครื่องมือที่หลากหลายได้

ตัวอย่างข้างต้นสามารถดำเนินการได้โดยไม่ต้อง monads? แน่นอนว่าพวกเขาสามารถนำไปใช้ในรูปแบบ Ad-hoc ได้ แต่การมี monads ที่สร้างขึ้นในภาษานั้นจะช่วยให้สามารถรองรับการลงคะแนนแบบโดยตรงเช่นdo-notation ซึ่งใช้ได้กับ monads ทั้งหมด


2

สิ่งหนึ่งที่ทำให้สับสนคือฟังก์ชั่น "ที่เป็นที่นิยม" ชอบbindและให้<*>ความสำคัญกับ Praxis แต่เพื่อให้เข้าใจแนวคิดมันง่ายกว่าที่จะดูฟังก์ชั่นอื่นก่อน นอกจากนี้ยังเป็นที่น่าสังเกตว่าพระสงฆ์โดดเด่นเพราะพวกเขามีน้ำหนักมากเกินไปเมื่อเทียบกับแนวคิดที่เชื่อมโยงอื่น ๆ ดังนั้นฉันจะเริ่มด้วยฟังก์ชั่นแทน

Functors มีฟังก์ชั่น (ในสัญกรณ์ fmap :: (Functor f) => (a -> b) -> f a -> f bHaskell) คุณมีบริบทfที่คุณสามารถยกฟังก์ชันขึ้นมาได้ อย่างที่คุณจินตนาการได้ว่าเกือบทุกอย่างเป็นนักแสดง รายการ, บางที, อย่างใดอย่างหนึ่ง, ฟังก์ชั่น, I / O, สิ่งอันดับ, ตัวแยกวิเคราะห์ ... แต่ละรายการแสดงถึงบริบทที่ค่าสามารถปรากฏขึ้น ดังนั้นคุณสามารถเขียนมากหลากหลายฟังก์ชั่นที่ทำงานในเกือบทุกบริบทโดยใช้หรือตัวแปรแบบอินไลน์ของมันfmap<$>

คุณต้องการทำอะไรกับบริบทอื่นอีกบ้าง คุณอาจต้องการรวมสองบริบท ดังนั้นคุณอาจต้องการที่จะได้รับลักษณะทั่วไปของตัวอย่างเช่นนี้zip :: [a] -> [b] -> [(a,b)]pair :: (Monoidal f) => f a -> f b -> f (a,b)

แต่เพราะมันมีประโยชน์มากขึ้นในทางปฏิบัติห้องสมุด Haskell แทนที่จะนำเสนอApplicativeซึ่งเป็นส่วนผสมของFunctorและMonoidalและยังUnitซึ่งเพียงแค่เพิ่มที่คุณสามารถใส่จริงค่า "ภายใน" unitบริบทของคุณด้วย

คุณสามารถเขียนฟังก์ชั่นที่ธรรมดามากโดยเพียงระบุสามสิ่งเหล่านี้เกี่ยวกับบริบทที่คุณกำลังทำงาน

Monadเป็นเพียงอีกสิ่งหนึ่งที่คุณสามารถระบุได้ สิ่งที่ฉันไม่ได้พูดถึงก่อนหน้านี้คือคุณมีสองวิธีในการรวมสองบริบท: คุณไม่สามารถทำได้เพียงpairแต่คุณยังสามารถเรียงซ้อนได้เช่นคุณสามารถมีรายการได้ ในบริบทของ I / O ตัวอย่างจะดำเนินการ I / O ที่สามารถอ่านการกระทำอื่น ๆ ของ I / O FilePath -> IO (IO a)จากแฟ้มดังนั้นคุณจะต้องพิมพ์ เราจะกำจัดการสแต็กนั้นเพื่อให้ได้ฟังก์ชั่นการปฏิบัติการได้IO aอย่างไร? นั่นคือสิ่งที่Monads เข้าjoinมาช่วยให้เราสามารถรวมบริบทที่ซ้อนกันสองประเภทเดียวกัน เช่นเดียวกันสำหรับ parsers อาจจะเป็น ฯลฯ และbindเป็นเพียงวิธีที่ใช้งานได้จริงjoin

ดังนั้นบริบทของ monadic เพียงเสนอสี่สิ่งและสามารถใช้ได้กับเครื่องจักรเกือบทั้งหมดที่พัฒนาขึ้นสำหรับ I / O สำหรับ parsers สำหรับความล้มเหลว ฯลฯ


1

Monads ให้คุณแสดงการคำนวณที่ไม่บริสุทธิ์ต่างๆรวมถึงการทำให้โค้ดง่ายขึ้น

  • การคำนวณแบบ stateful (รับ / กำหนดสถานะผ่าน monad)
  • I / O (การบันทึก, UI, ไฟล์หรือเพียงสร้าง / บริโภครายการ X)
  • นอกจากนี้การควบคุมโฟลว์ "ที่ไม่ใช่เชิงเส้น" (เช่นข้อยกเว้นอาจจะเป็นต้น)

และที่สำคัญไม่มีการประนีประนอมกับภาษาบริสุทธิ์ที่สร้างขึ้นและทำให้ภาษาสะอาดขึ้น


2
จริง - แต่นั่นเป็นเพียงส่วนหนึ่งของสิ่งที่พระใช้สำหรับ รายการความเข้าใจหรือMaybeไม่เกี่ยวข้องกับสิ่งภายนอก
JacquesB

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

@JacquesB พวกเขาเป็นอย่างไรรัฐ
จูลส์

1
ประเภทโลก, ประเภทที่ไม่เหมือนใคร, ประเภทลิเนียร์ช่วยให้คุณทำเช่นนั้นได้
Jörg W Mittag

@Jules มีสถานะเป็น nondeterminism monad หรือเปล่า? คุณช่วยอธิบายความหมายของคำว่า "stateful" ได้ไหม?
แจ็ค
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.