พยายามจะเข้าใจโมนาด ›› ผู้ปฏิบัติงาน

ฉันเพิ่งเริ่มใช้ Haskell และกำลังเรียนรู้ผ่าน LearnYouAHaskell ฉันไม่เข้าใจเหตุผลเบื้องหลังตัวดำเนินการ (>>)
การใช้งานเริ่มต้นคือ:

(>>) :: (Monad m) => m a -> m b -> m b  
m >> n = m >>= \_ -> n 

ซึ่ง (เท่าที่ฉันเข้าใจ) จะละเว้นค่าแรกและส่งกลับค่าที่สอง อย่างไรก็ตาม จากตัวอย่างใน LearnYouAHaskell สิ่งนี้จะเกิดขึ้น:

ghci> ไม่มีอะไร >> แค่ 3
ไม่มีอะไร
ghci> แค่ 3 >> ไม่มีอะไร
ไม่มีอะไร

ดังนั้นจึง ไม่ ละเว้นค่าแรก แต่จากการวิจัยเล็กๆ น้อยๆ ฉันพบคำพูดนี้จาก ที่นี่

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

ดังนั้นฉันจึง งง เกี่ยวกับการใช้งานโอเปอเรเตอร์นี้ และฉันต้องการถาม 2 สิ่ง:

  1. มันทำอะไร จริงๆ แล้วทำอะไร?
  2. เมื่อใด จะมีประโยชน์?

person Tzah Mama    schedule 20.07.2014    source แหล่งที่มา


คำตอบ (5)


ฟังก์ชัน >> ละเว้นเฉพาะผลลัพธ์ของค่าแรก แต่ ไม่ ละเว้นผลข้างเคียงของค่าแรก เพื่อทำความเข้าใจตัวอย่างของคุณ โปรดดูวิธีกำหนด Maybe Monad:

instance Monad Maybe where
  return = Just
  (Just x) >>= f = f x
  Nothing >>= _ = Nothing

และฟังก์ชัน >> ถูกกำหนดไว้ดังนี้:

m >> k      = m >>= \_ -> k

Nothing >> _ จะผลิต Nothing ตามคำจำกัดความของ Maybe monad ในตัวอย่างที่สองของคุณ Just 3 >> Nothing ได้รับการขยายเป็น Just 3 >>= \_ -> Nothing และสร้าง Nothing เพื่อให้เป็นตัวอย่างว่าระบบจะละเว้นค่าของการกระทำแรกเท่านั้นแต่ไม่ได้ละเว้นผลข้างเคียงอย่างไร ให้พิจารณาตัวอย่างต่อไปนี้:

λ> print 3 >> print 4
3
4

คุณจะเห็นได้จากตัวอย่างข้างต้น แม้ว่ามันจะละเว้นผลลัพธ์ของ print 3 ซึ่งก็คือ () แต่ก็ไม่ได้ละเลยผลข้างเคียงของมันซึ่งก็คือการแสดง 3 บนหน้าจอ

ฟังก์ชัน >> จะมีประโยชน์เมื่อคุณเริ่มใช้ไลบรารี Haskell อื่น สองที่ที่ฉันใช้เป็นครั้งคราวคือเมื่อต้องจัดการกับ Parsers (parsec, attoparsec) และไลบรารี Pipes

person Sibi    schedule 20.07.2014

โดยจะละเว้นคุณค่าของการกระทำแรก ไม่ใช่ตัวการกระทำเอง

Just 3 >> Just 5

ค่าของการกระทำ Just 3 คือ 3 มันจะถูกละเว้นในส่วน \_ -> n ผลลัพธ์โดยรวมคือ Just 5

Just 3 >> Nothing

ค่าของการกระทำ Just 3 คือ 3 มันถูกละเว้นในส่วน \_ -> n ผลลัพธ์โดยรวมคือ Nothing

Nothing >> Just 3

การกระทำ Nothing ไม่ได้สร้างค่าใดๆ เลย แล้วมันจะส่งผ่านไปยังตัวถูกดำเนินการทางขวาของ >>= (หรือ >>) อย่างไร? มันไม่ได้! >>= สำหรับ Maybe monad ถูกสร้างขึ้นเพื่อให้หากการกระทำด้านซ้ายคือ Nothing การกระทำที่ถูกต้องจะไม่ถูกดำเนินการเลย และผลลัพธ์โดยรวมคือ Nothing

person n. 1.8e9-where's-my-share m.    schedule 20.07.2014

เพื่อให้คำตอบของ Sibi สมบูรณ์ >> สามารถดูได้ว่าเป็น ; ในภาษาอื่นเช่น C หรือ C++..

เมื่อคุณทำในภาษา C (หรือเทียบเท่าในภาษาอื่น)

printf("ฟู"); printf("บาร์");

เห็นได้ชัดว่าคุณพิมพ์ foobar (ผลข้างเคียง) แต่การเรียก printf เหล่านั้นก็มีค่าตอบแทนเช่นกัน ซึ่งในกรณีของเราคือความยาวที่กำลังพิมพ์ เช่น 3 3 คุณเคยสงสัยหรือไม่ว่าเกิดอะไรขึ้นกับตัวเลขเหล่านั้น พวกเขาถูกทิ้งไป เพราะใน C, expr 1; ประสบการณ์ 2 หมายถึง

  • ประเมิน expr1
  • ทิ้งผลลัพธ์ของมันไป
  • ประเมิน expr2

(ณ จุดนั้น คุณอาจถามตัวเองว่าทำไมคอมไพเลอร์ถึงต้องกังวลกับการประเมิน expr1 ถ้ามันต้องทิ้งผลลัพธ์ของมันไป ? เนื่องจากผลข้างเคียง ในกรณีของ printf ผลข้างเคียงคือการพิมพ์อะไรบางอย่าง คุณ ไม่ค่อยสนใจค่าที่ส่งคืนเอง)

ดังนั้น ; จึงถูกมองว่าเป็นตัวดำเนินการที่ใช้ 2 นิพจน์และส่งคืนนิพจน์ใหม่ นี่เหมือนกับสิ่งที่ตัวดำเนินการ >> ทำทุกประการ

เมื่อคุณเขียน

 print "foo" >> print "bar"

มันเทียบเท่ากับ printf("foo");printf("bar") ทุกประการ ยกเว้น (และนั่นคือความแตกต่างที่สำคัญ) >> ไม่ใช่สิ่งมหัศจรรย์เช่น ; ใน C >> เป็นตัวดำเนินการที่ผู้ใช้กำหนดและสามารถกำหนดใหม่สำหรับ Monad แต่ละประเภทได้ นี่คือเหตุผลที่โปรแกรมเมอร์ Haskell รัก Monad มาก กล่าวโดยสรุป มันช่วยให้คุณกำหนดพฤติกรรม ; ของคุณเองใหม่ได้

ดังที่เราเห็น ใน C ; เพียงแค่ประเมินนิพจน์และละทิ้งค่าของมันไป อันที่จริง มันซับซ้อนกว่าเล็กน้อยเพราะไม่ใช่ break หรือ return ไม่มีสิ่งใดในโมนาดเมย์ที่สามารถเห็นได้ว่าเป็น break หรือ return >> ประเมินนิพจน์แรก และหยุดหากเป็น Nothing มิฉะนั้นจะละทิ้งคุณค่าและดำเนินต่อไป

ตัวอย่างแรกของคุณสามารถพบได้ใน C (ฉันคิดว่ามันถูกต้อง C)

3; return

และ

return; 3

ตัวอย่างแรก คำนวณ 3 ละทิ้งค่าและส่งกลับ ประการที่สองกลับมาทันที

เพื่อตอบคำถามของคุณ when is it usefull ? เกือบตลอดเวลาที่คุณใช้ IO แม้ว่าคุณจะไม่ค่อยเห็นมันก็ตาม

แทนที่จะเขียน

 print "foo" >> print "bar"

Haskell จัดเตรียมน้ำตาลวากยสัมพันธ์ซึ่งจะแปลงบรรทัดใหม่ (ค่อนข้างมาก) เป็น >> ผ่านทาง do-notation ดังนั้นคุณจะเขียน

do
  print "foo"
  print "bar"

ซึ่งเทียบเท่ากับเวอร์ชันก่อนหน้าอย่างเคร่งครัด (ความจริงแล้วเวอร์ชันสัญลักษณ์ doation จะถูกแปลงเป็นเวอร์ชันก่อนหน้าโดยคอมไพเลอร์)

มันยังเทียบเท่ากับ (แม้ว่าจะไม่ค่อยได้ใช้ก็ตาม)

do print "foo"; print "bar"

โดยสรุป >> สามารถมองได้ว่าเทียบเท่ากับ ; หรือการขึ้นบรรทัดใหม่เป็นภาษาอื่นที่มีความแตกต่างว่าความหมายที่แน่นอนนั้นขึ้นอยู่กับบริบท (ที่ Monad กำลังดำเนินการอยู่) >> ในบางที monad แตกต่างจาก >> ใน IO Monad

person mb14    schedule 20.07.2014

ก่อนอื่น มาเคลียร์ความสับสนของคุณใน Maybe monad กันก่อน พิจารณาสิ่งต่อไปนี้

instance Monad Maybe where
  return = Just
  (Just x) >>= g = g x
  Nothing  >>= _ = Nothing

ดังที่คุณเห็นตามคำจำกัดความ Nothing >>= _ คือ Nothing เนื่องจาก >> เป็นเพียงกรณีพิเศษของ >>= โดยที่พารามิเตอร์ถูกละเว้น ผลลัพธ์จึงเหมือนเดิม

เนื่องจากโดยปกติแล้ว Maybes จะใช้เพื่อแสดงการคำนวณที่อาจล้มเหลว นี่คือการบอกเราว่า "เมื่อคุณล้มเหลว คุณจะล้มเหลวเสมอ"

ตอนนี้เพื่อตอบคำถามของคุณ

  1. คำพูดที่คุณพูดถึงก็ตอบไปแล้ว
  2. ซึ่งจะมีประโยชน์ในบางสถานการณ์ที่ไม่จำเป็นต้องมีผลลัพธ์ของการดำเนินการ ตัวอย่างเช่น ผลลัพธ์ของ putStrLn คือ () ซึ่งไม่น่าสนใจและไม่มีประโยชน์แต่อย่างใด
person Evan Sebastian    schedule 20.07.2014

เหตุผลนั้นง่ายมาก: คำง่ายๆ มีสองการดำเนินการที่จำเป็นสำหรับการใช้งาน monad:

  • >>=
  • >>

การดำเนินการครั้งแรกและส่งผลของการกระทำไปยังการดำเนินการอื่น ตัวอย่างเช่น:

Prelude> :t getLine
getLine :: IO String
Prelude> :t putStrLn
putStrLn :: String -> IO ()

สองฟังก์ชัน: แรก getLine เพียงส่งคืน String ที่ห่อด้วย IO มันจะอ่านสตริงจาก stdin และรวมไว้ที่ IO putStrLn อันที่สองได้รับ String และพิมพ์ออกมา Bind มีประเภทดังต่อไปนี้:

:t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b

สามารถแสดงเป็น IO String -> (String -> IO String) -> IO String ได้ ดังนั้นคุณจึงสามารถรวมฟังก์ชันตั้งแต่ 2 ฟังก์ชันขึ้นไปเข้ากับ (>>=) ได้โดยดำเนินการ getLine และส่งผลลัพธ์ String ไปที่ putStrLn:

getLine >>= putStrLn

ดังนั้นคุณจึงสามารถรวมฟังก์ชันสองฟังก์ชันขึ้นไปไว้ในการดำเนินการเดียวได้

>> อันที่สองให้ผลลัพธ์เกือบจะเหมือนกัน แต่ไม่จำเป็นต้องเป็นผลจากการกระทำครั้งก่อน

:t (>>)
(>>) :: Monad m => m a -> m b -> m b

มันแค่ดำเนินการ การกระทำแรก และการกระทำที่สอง และการกระทำที่สองไม่จำเป็นสำหรับผลลัพธ์ของการกระทำครั้งแรก:

putStrLn "Hello" >> putStrLn "World"
person 0xAX    schedule 20.07.2014