แพคเกจไม่ได้ส่งออกคอนสตรัคข้อมูลControl.Monad.Writer
Writer
ฉันเดาว่าสิ่งนี้แตกต่างกันเมื่อเขียน LYAH
ใช้เครื่องพิมพ์ดีด MonadWriter ใน ghci
คุณสร้างนักเขียนโดยใช้writer
ฟังก์ชันแทน ตัวอย่างเช่นในเซสชัน ghci ฉันสามารถทำได้
ghci> import Control.Monad.Writer
ghci> let logNumber x = writer (x, ["Got number: " ++ show x])
ตอนนี้logNumber
เป็นฟังก์ชันที่สร้างนักเขียน ฉันสามารถขอประเภท:
ghci> :t logNumber
logNumber :: (Show a, MonadWriter [String] m) => a -> m a
ซึ่งบอกฉันว่าประเภทที่อนุมานไม่ใช่ฟังก์ชันที่ส่งกลับตัวเขียนเฉพาะแต่เป็นสิ่งที่ใช้MonadWriter
คลาส type ตอนนี้ฉันสามารถใช้มันได้แล้ว:
ghci> let multWithLog = do { a <- logNumber 3; b <- logNumber 5; return (a*b) }
:: Writer [String] Int
(อินพุตจริงป้อนทั้งหมดในบรรทัดเดียว) นี่ฉันได้ระบุชนิดของการที่จะเป็นmultWithLog
Writer [String] Int
ตอนนี้ฉันสามารถรันได้แล้ว:
ghci> runWriter multWithLog
(15, ["Got number: 3","Got number: 5"])
และคุณจะเห็นว่าเราบันทึกการดำเนินการขั้นกลางทั้งหมด
ทำไมโค้ดถึงเขียนแบบนี้?
ทำไมต้องสร้างMonadWriter
คลาสประเภทเลย? เหตุผลคือต้องทำกับหม้อแปลง monad ตามที่คุณทราบอย่างถูกต้องวิธีที่ง่ายที่สุดในการนำไปใช้Writer
คือการใช้กระดาษห่อหุ้มแบบใหม่ที่ด้านบนของคู่:
newtype Writer w a = Writer { runWriter :: (a,w) }
คุณสามารถประกาศอินสแตนซ์ monad สำหรับสิ่งนี้แล้วเขียนฟังก์ชัน
tell :: Monoid w => w -> Writer w ()
ซึ่งเพียงแค่บันทึกข้อมูลเข้า ตอนนี้สมมติว่าคุณต้องการ monad ที่มีความสามารถในการบันทึก แต่ก็ทำอย่างอื่นได้เช่นกันบอกว่าสามารถอ่านจากสภาพแวดล้อมได้เช่นกัน คุณจะใช้สิ่งนี้เป็น
type RW r w a = ReaderT r (Writer w a)
ตอนนี้เนื่องจากตัวเขียนอยู่ในReaderT
หม้อแปลง monad หากคุณต้องการบันทึกเอาท์พุทคุณไม่สามารถใช้งานได้tell w
(เพราะใช้งานได้เฉพาะกับนักเขียนที่ไม่ได้ห่อหุ้ม) แต่คุณต้องใช้lift $ tell w
ซึ่ง "ยก" tell
ฟังก์ชันผ่านReaderT
เพื่อให้สามารถเข้าถึง นักเขียนภายใน monad หากคุณต้องการสองชั้นหม้อแปลง (ว่าคุณต้องการที่จะเพิ่มการจัดการข้อผิดพลาดเช่นกัน) lift $ lift $ tell w
แล้วคุณจะต้องใช้ สิ่งนี้ทำให้เทอะทะได้อย่างรวดเร็ว
แต่โดยการกำหนดคลาสประเภทเราสามารถสร้าง monad transformer wrapper รอบ ๆ ตัวเขียนให้กลายเป็นตัวอย่างของตัวเขียน ตัวอย่างเช่น,
instance (Monoid w, MonadWriter w m) => MonadWriter w (ReaderT r m)
นั่นคือถ้าw
เป็นหนังสือและm
เป็นMonadWriter w
แล้วนอกจากนี้ยังมีReaderT r m
MonadWriter w
ซึ่งหมายความว่าเราสามารถใช้tell
ฟังก์ชั่นนี้ได้โดยตรงบนโมนาดที่เปลี่ยนรูปโดยไม่ต้องกังวลกับการยกมันผ่านหม้อแปลงโมนาด