IO monad ทางเทคนิคไม่ถูกต้องหรือไม่?


12

บนวิกิ Haskell มีตัวอย่างต่อไปของการใช้งานตามเงื่อนไขของ IO monad (ดูที่นี่)

when :: Bool -> IO () -> IO ()
when condition action world =
    if condition
      then action world
      else ((), world)

โปรดทราบว่าในตัวอย่างนี้คำจำกัดความของIO aถูกนำไปใช้RealWorld -> (a, RealWorld)เพื่อทำให้ทุกอย่างเข้าใจง่ายขึ้น

ตัวอย่างนี้เรียกใช้การกระทำใน IO monad แบบมีเงื่อนไข ตอนนี้สมมติว่านั่นconditionคือFalseการกระทำที่actionไม่ควรดำเนินการ ใช้ความหมายสันหลังยาวนี่จะเป็นกรณี อย่างไรก็ตามมีการบันทึกไว้ที่นี่ว่า Haskell พูดถึงเทคนิคที่ไม่เข้มงวด ซึ่งหมายความว่าคอมไพเลอร์ได้รับอนุญาตให้รันaction worldบนเธรดอื่นที่แตกต่างกันและจากนั้นทิ้งการคำนวณนั้นเมื่อพบว่าไม่ได้ต้องการมัน อย่างไรก็ตามตามจุดนั้นผลข้างเคียงจะเกิดขึ้นแล้ว

ในตอนนี้เราอาจนำ IO monad ไปใช้ในลักษณะที่ผลข้างเคียงจะแพร่กระจายเมื่อโปรแกรมทั้งหมดเสร็จสิ้นและเรารู้ว่าควรใช้ผลข้างเคียงใด อย่างไรก็ตามนี่ไม่ใช่กรณีเนื่องจากเป็นไปได้ที่จะเขียนโปรแกรมที่ไม่มีที่สิ้นสุดใน Haskell ซึ่งมีผลข้างเคียงปานกลางอย่างชัดเจน

นี่หมายความว่า IO monad ผิดทางเทคนิคหรือมีสิ่งอื่นที่ทำให้เกิดปัญหานี้หรือไม่?


ยินดีต้อนรับสู่วิทยาการคอมพิวเตอร์ ! คำถามของคุณอยู่นอกหัวข้อที่นี่: เราจัดการกับคำถามวิทยาการคอมพิวเตอร์ไม่ใช่คำถามเกี่ยวกับการเขียนโปรแกรม (ดูคำถามที่พบบ่อยของเรา) คำถามของคุณอาจจะมีในหัวข้อเกี่ยวกับกองมากเกิน
dkaeae

2
ในความเห็นของฉันนี่เป็นคำถามวิทยาศาสตร์คอมพิวเตอร์เพราะมันเกี่ยวกับความหมายเชิงทฤษฎีของ Haskell ไม่ใช่คำถามการเขียนโปรแกรมเชิงปฏิบัติ
Lasse

4
ฉันไม่คุ้นเคยกับทฤษฎีภาษาโปรแกรม แต่ฉันคิดว่าคำถามนี้อยู่ในหัวข้อที่นี่ มันอาจช่วยได้ถ้าคุณชี้แจงว่า 'ผิด' หมายถึงอะไรที่นี่ คุณคิดว่าคุณสมบัติของ IO monad มีคุณสมบัติใดที่ไม่ควรมี
จิ้งจกไม่ต่อเนื่อง

1
โปรแกรมนี้พิมพ์ไม่ถูกต้อง ฉันไม่แน่ใจว่าคุณตั้งใจจะเขียนอะไร คำจำกัดความของwhenสามารถพิมพ์ได้ แต่ไม่มีประเภทที่คุณประกาศและฉันไม่เห็นสิ่งที่ทำให้รหัสนี้น่าสนใจ
Gilles 'ดังนั้นหยุดความชั่วร้าย'

2
โปรแกรมนี้ใช้คำต่อคำจากหน้า Haskell-wiki ที่ลิงก์โดยตรงด้านบน มันไม่ได้พิมพ์แน่นอน นี่เป็นเพราะมันถูกเขียนภายใต้สมมติฐานที่IO aกำหนดไว้RealWorld -> (a, RealWorld)เพื่อให้ internals ของ IO สามารถอ่านได้มากขึ้น
Lasse

คำตอบ:


12

นี่คือ "การตีความ" ที่แนะนำของIOmonad หากคุณต้องการ "ตีความ" อย่างจริงจังคุณจำเป็นต้องใช้ "โลกแห่งความจริง" อย่างจริงจัง มันไม่เกี่ยวข้องว่าจะaction worldได้รับการประเมินแบบพิเศษหรือไม่actionไม่มีผลข้างเคียงใด ๆ ผลกระทบของมันจะถูกจัดการโดยการคืนสถานะใหม่ของเอกภพที่เกิดผลกระทบเหล่านั้นเช่นส่งแพ็กเก็ตเครือข่าย อย่างไรก็ตามผลของการทำงานเป็นและดังนั้นจึงรัฐใหม่ของจักรวาลอยู่((),world) worldเราไม่ได้ใช้จักรวาลใหม่ที่เราอาจประเมินด้านเก็งกำไรด้านข้าง worldรัฐของจักรวาลอยู่

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

"รอเดี๋ยวก่อน" คุณพูด " RealWorldเป็นเพียง 'โทเค็น'. มันไม่จริงรัฐของจักรวาลทั้งหมด." โอเค "การตีความ" นี้ไม่ได้อธิบายอะไรเลย อย่างไรก็ตามในฐานะที่เป็นรายละเอียดการดำเนินการIOนี้เป็นวิธีที่รุ่น GHC 1อย่างไรก็ตามนี่หมายความว่าเรามี "ฟังก์ชั่น" ที่น่าอัศจรรย์ซึ่งมีผลข้างเคียงและโมเดลนี้ไม่มีคำแนะนำเกี่ยวกับความหมายของมัน และเนื่องจากฟังก์ชั่นเหล่านี้มีผลข้างเคียงจริง ๆ ความกังวลของคุณเพิ่มขึ้นอย่างชัดเจน GHC จะต้องออกไปเพื่อให้แน่ใจRealWorldและฟังก์ชั่นพิเศษเหล่านี้จะไม่ได้รับการปรับให้เหมาะสมในวิธีที่เปลี่ยนแปลงพฤติกรรมตามที่ตั้งใจไว้ของโปรแกรม

โดยส่วนตัว (ดังที่เห็นได้ชัดในตอนนี้) ฉันคิดว่ารูปแบบ "การผ่านโลก" ของIOไร้ประโยชน์และสับสนในฐานะเครื่องมือการสอน (ไม่ว่าจะมีประโยชน์สำหรับการนำไปใช้งานฉันไม่รู้สำหรับ GHC ฉันคิดว่ามันเป็นสิ่งประดิษฐ์ทางประวัติศาสตร์มากกว่า)

วิธีการทางเลือกหนึ่งคือดูIOเป็นคำขออธิบายที่มีตัวจัดการการตอบกลับ มีหลายวิธีในการทำเช่นนี้ อาจเป็นไปได้มากที่สุดคือการใช้การก่อสร้าง monad ฟรีโดยเฉพาะเราสามารถใช้:

data IO a = Return a | Request OSRequest (OSResponse -> IO a)

มีหลายวิธีที่จะทำให้สิ่งนี้ซับซ้อนมากขึ้นและมีคุณสมบัติที่ค่อนข้างดีกว่า แต่นี่เป็นการปรับปรุง ไม่จำเป็นต้องมีสมมติฐานเชิงลึกเกี่ยวกับธรรมชาติของความเป็นจริงที่จะเข้าใจ สิ่งที่กล่าวมาทั้งหมดนั้นIOเป็นเพียงโปรแกรมเล็ก ๆ น้อย ๆReturnที่ไม่ทำอะไรนอกจากคืนค่าหรือเป็นคำร้องขอไปยังระบบปฏิบัติการพร้อมตัวจัดการสำหรับการตอบสนอง OSRequestสามารถเป็นสิ่งที่ชอบ:

data OSRequest = OpenFile FilePath | PutStr String | ...

ในทำนองเดียวกันOSResponseอาจเป็นสิ่งที่ชอบ:

data OSResponse = Errno Int | OpenSucceeded Handle | ...

(หนึ่งในการปรับปรุงที่สามารถทำได้คือการทำให้สิ่งที่ปลอดภัยมากขึ้นเพื่อให้คุณรู้ว่าคุณจะไม่ได้รับOpenSucceededจากการPutStrร้องขอ) รุ่นนี้IOเป็นการอธิบายคำขอที่ถูกตีความโดยบางระบบ (สำหรับ "จริง" IOmonad นี่คือ รันไทม์ของ Haskell เอง) จากนั้นบางทีระบบนั้นจะเรียกผู้จัดการที่เราให้มาพร้อมกับการตอบกลับ แน่นอนว่าสิ่งนี้ไม่ได้บ่งชี้ว่าPutStr "hello world"ควรจัดการกับคำร้องขออย่างไร แต่ก็ไม่ได้แสร้งทำ มันทำให้ชัดเจนว่านี่คือการมอบหมายให้ระบบอื่น ๆ รุ่นนี้ก็ค่อนข้างแม่นยำ โปรแกรมผู้ใช้ทั้งหมดในระบบปฏิบัติการที่ทันสมัยต้องทำการร้องขอไปยังระบบปฏิบัติการเพื่อทำสิ่งใด

รุ่นนี้ให้สัญชาติญาณที่ถูกต้อง ตัวอย่างเช่นผู้เริ่มต้นจำนวนมากดูสิ่งต่าง ๆ เช่น<-โอเปอเรเตอร์เป็น "คลาย" IOหรือมีมุมมอง (เสริมโชคร้าย) ที่ว่าIO String, พูด, เป็น "คอนเทนเนอร์" ที่ "มี" Strings (แล้ว<-ดึงออก) มุมมองการตอบสนองคำขอนี้ทำให้มุมมองนี้ผิดอย่างชัดเจน OpenFile "foo" (\r -> ...)ไม่มีในแฟ้มจับเป็น การเปรียบเทียบทั่วไปเพื่อเน้นสิ่งนี้คือไม่มีเค้กอยู่ภายในสูตรสำหรับเค้ก (หรืออาจเป็น "ใบแจ้งหนี้" จะดีกว่าในกรณีนี้)

รุ่นนี้ยังใช้งานได้พร้อมกับการทำงานพร้อมกัน เราสามารถมี Constructor for OSRequestlike ได้อย่างง่ายดายFork :: (OSResponse -> IO ()) -> OSRequestและจากนั้นรันไทม์สามารถสอดแทรกคำร้องขอที่สร้างโดยตัวจัดการพิเศษนี้พร้อมตัวจัดการปกติ แต่ก็ชอบ ด้วยความเฉลียวฉลาดคุณสามารถใช้สิ่งนี้ (หรือเทคนิคที่เกี่ยวข้อง) เพื่อจำลองสิ่งต่าง ๆ เช่นการเกิดพร้อมกันโดยตรงแทนที่จะพูดว่า "เราขอให้ระบบปฏิบัติการและสิ่งต่าง ๆ เกิดขึ้น" นี่คือวิธีการทำงานของIOSpecห้องสมุด

1 Hugs ใช้การดำเนินการตามความต่อเนื่องIOซึ่งคล้ายกับสิ่งที่ฉันอธิบายถึงแม้ว่าด้วยฟังก์ชั่นทึบแสงแทนประเภทข้อมูลที่ชัดเจน นอกจากนี้ HBC ยังใช้การติดตั้งแบบต่อเนื่องตามชั้นบน IO ที่ใช้การตอบสนองการร้องขอกระแส NHC (และ YHC) ใช้ thunks นั่นคือคร่าวๆIO a = () -> aแม้ว่าจะ()ถูกเรียกWorldแต่ก็ไม่ได้ผ่านการทำรัฐ JHC และ UHC นั้นใช้วิธีเดียวกันกับ GHC


ขอบคุณสำหรับคำตอบที่รู้แจ้งของคุณมันช่วยได้จริงๆ การใช้งาน IO ของคุณใช้เวลาสักครู่เพื่อตัดใจ แต่ฉันยอมรับว่ามันใช้งานง่ายกว่า คุณอ้างว่าการติดตั้งนี้ไม่ประสบปัญหาที่อาจเกิดขึ้นจากการสั่งซื้อผลข้างเคียงเช่นการใช้งาน RealWorld หรือไม่ ฉันไม่สามารถเห็นปัญหาใด ๆ ได้ทันที แต่ก็ไม่ชัดเจนสำหรับฉันว่าพวกเขาไม่มีอยู่จริง
Lasse

ความคิดเห็นหนึ่ง: ดูเหมือนว่าOpenFile "foo" (\r -> ...)ควรจะเป็นจริงRequest (OpenFile "foo") (\r -> ...)?
Lasse

@Lasse Requestอ๋อมันควรจะได้รับด้วย ในการตอบคำถามแรกของคุณคำสั่งนี้IOไม่มีความชัดเจนต่อการประเมินผล (พื้นแบบโมดูโล) เนื่องจากเป็นค่าเฉื่อย ผลข้างเคียงทั้งหมด (ถ้ามี) จะทำโดยสิ่งที่ตีความค่านี้ ในwhenตัวอย่างมันไม่สำคัญว่าจะได้actionรับการประเมินหรือไม่เพราะมันจะเป็นค่าRequest (PutStr "foo") (...)ที่เราไม่ให้กับสิ่งที่ตีความคำขอเหล่านี้ต่อไป มันเหมือนกับซอร์สโค้ด ไม่สำคัญว่าคุณจะลดความกระหายหรือความเกียจคร้านไม่มีอะไรเกิดขึ้นจนกว่ามันจะได้รับล่าม
Derek Elkins ออกจาก SE

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