จำนวนการดู # 1 และ # 2 ไม่ถูกต้อง
- ประเภทข้อมูลใด ๆ
* -> *สามารถทำงานเป็นป้ายกำกับพระได้มากกว่านั้น
- (ยกเว้นของ
IOmonad) การคำนวณภายใน monad นั้นไม่บริสุทธิ์ มันเป็นเพียงการคำนวณที่เราเห็นว่ามีผลข้างเคียง แต่บริสุทธิ์
ความเข้าใจผิดทั้งสองนี้มาจากการมุ่งเน้นไปที่IOmonad ซึ่งจริงๆแล้วค่อนข้างพิเศษ
ฉันจะพยายามอธิบายอย่างละเอียดในข้อที่ 3 เล็กน้อยโดยไม่ต้องคำนึงถึงทฤษฎีหมวดหมู่หากเป็นไปได้
การคำนวณมาตรฐาน
f :: a -> bการคำนวณทั้งหมดในภาษาเขียนโปรแกรมการทำงานสามารถดูเป็นฟังก์ชั่นที่มีประเภทแหล่งที่มาและประเภทเป้าหมาย: หากฟังก์ชั่นมีมากกว่าหนึ่งอาร์กิวเมนต์เราสามารถแปลงมันเป็นฟังก์ชั่นหนึ่งอาร์กิวเมนต์โดยการปิดกั้น (ดูHaskell wiki ) และถ้าเรามีเพียงค่าx :: a(ฟังก์ชั่นที่มี 0 อาร์กิวเมนต์) เราสามารถแปลงเป็นฟังก์ชันที่รับอาร์กิวเมนต์ของหน่วยประเภท :(\_ -> x) :: () -> a :
เราสามารถสร้างโปรแกรมที่ซับซ้อนมากขึ้นในรูปแบบที่ง่ายขึ้นโดยการเขียนฟังก์ชั่นดังกล่าวโดยใช้.โอเปอเรเตอร์ ตัวอย่างเช่นถ้าเรามีf :: a -> bและเราได้รับg :: b -> c g . f :: a -> cโปรดทราบว่างานนี้ได้ค่าแปลงของเรามากเกินไป: ถ้าเรามีและแปลงเป็นตัวแทนของเราที่เราได้รับx :: af . ((\_ -> x) :: () -> a) :: () -> b
การเป็นตัวแทนนี้มีคุณสมบัติที่สำคัญมาก ได้แก่ :
- เรามีฟังก์ชั่นพิเศษมาก - ตัวตนของฟังก์ชั่นสำหรับแต่ละประเภท
id :: a -> a aมันเป็นเอกลักษณ์องค์ประกอบ ที่เกี่ยวกับ.: fเท่ากับทั้งสองและf . idid . f
- ผู้ประกอบการองค์ประกอบการทำงาน
.เป็นแบบเชื่อมโยง
การคำนวณแบบ monadic
สมมติว่าเราต้องการเลือกและทำงานกับการคำนวณหมวดหมู่พิเศษบางอย่างซึ่งผลลัพธ์มีบางสิ่งมากกว่าเพียงค่าส่งคืนเดียว เราไม่ต้องการระบุความหมายของ "สิ่งที่มากกว่า" เราต้องการให้สิ่งต่าง ๆ โดยทั่วไปเป็นไปได้ วิธีทั่วไปมากที่สุดเพื่อเป็นตัวแทนของ "บางสิ่งบางอย่างมากขึ้น" เป็นตัวแทนเป็นฟังก์ชั่นชนิด - ประเภทmของชนิด* -> *(คือมันแปลงประเภทหนึ่งไปยังอีก) m :: * -> *ดังนั้นสำหรับแต่ละประเภทของการคำนวณเราต้องการที่จะทำงานร่วมกับเราจะมีฟังก์ชั่นบางชนิด (ใน Haskell, mคือ[], IO, Maybeฯลฯ ) a -> m bและประเภทที่ประสงค์มีฟังก์ชั่นทุกชนิด
ตอนนี้เราอยากจะทำงานกับฟังก์ชั่นในหมวดหมู่ดังกล่าวในลักษณะเดียวกันกับกรณีพื้นฐาน เราต้องการให้สามารถเขียนฟังก์ชั่นเหล่านี้ได้เราต้องการให้องค์ประกอบมีความสัมพันธ์และเราต้องการมีเอกลักษณ์ พวกเราต้องการ:
- จะมีผู้ประกอบการ (ขอเรียกว่า
<=<) ที่ประกอบด้วยฟังก์ชั่นf :: a -> m bและเป็นสิ่งที่เป็นg :: b -> m c g <=< f :: a -> m cและจะต้องมีการเชื่อมโยง
- จะมีฟังก์ชั่นบางตัวตนแต่ละประเภท,
returnขอเรียกว่า นอกจากนี้เรายังต้องการที่f <=< returnเป็นเช่นเดียวกับและเช่นเดียวกับfreturn <=< f
ใด ๆm :: * -> *ที่เรามีฟังก์ชั่นดังกล่าวreturnและ<=<เรียกว่าmonad มันช่วยให้เราสร้างการคำนวณที่ซับซ้อนจากคนที่เรียบง่ายเช่นเดียวกับในกรณีพื้นฐาน แต่ตอนนี้ประเภทของค่าผลตอบแทนจะ tranformed mโดย
(อันที่จริงฉันใช้คำว่าหมวดหมู่เล็กน้อยในทางที่ผิดในความหมายของหมวดหมู่ทฤษฎีเราสามารถเรียกหมวดหมู่การก่อสร้างของเราได้หลังจากที่เรารู้ว่ามันเป็นไปตามกฎหมายเหล่านี้)
Monads ใน Haskell
ใน Haskell (และภาษาการทำงานอื่น ๆ ) () -> aเราส่วนใหญ่ทำงานกับค่าไม่ได้กับฟังก์ชั่นประเภท ดังนั้นแทนที่จะกำหนด<=<สำหรับแต่ละ monad (>>=) :: m a -> (a -> m b) -> m bเรากำหนดฟังก์ชั่น คำจำกัดความทางเลือกดังกล่าวเทียบเท่าเราสามารถแสดงการ>>=ใช้<=<และในทางกลับกัน (ลองออกกำลังกายหรือดูแหล่งที่มา ) หลักการคือตอนนี้ชัดเจนน้อยลง แต่ก็ยังคงเหมือนเดิม: ผลของเราอยู่เสมอประเภทและเราเขียนฟังก์ชั่นประเภทm aa -> m b
สำหรับแต่ละ monad ที่เราสร้างเราต้องไม่ลืมที่จะตรวจสอบreturnและ<=<มีคุณสมบัติที่เราต้องการ: การเชื่อมโยงและตัวตนซ้าย / ขวา ใช้แสดงreturnและ>>=พวกเขาจะเรียกว่ากฎหมาย monad
ตัวอย่าง - รายการ
ถ้าเราเลือกmที่จะเป็นที่เราได้รับในประเภทของการทำงานของประเภท[] a -> [b]ฟังก์ชั่นดังกล่าวเป็นตัวแทนของการคำนวณที่ไม่ได้กำหนดค่าซึ่งผลลัพธ์อาจเป็นหนึ่งหรือหลายค่า แต่ก็ไม่มีค่าเช่นกัน นี้จะทำให้เกิดสิ่งที่เรียกว่าmonad รายการ องค์ประกอบf :: a -> [b]และการg :: b -> [c]ทำงานดังต่อไปนี้g <=< f :: a -> [c]หมายถึงการคำนวณผลลัพธ์ที่เป็นไปได้ทั้งหมด[b]นำgไปใช้กับแต่ละรายการและรวบรวมผลลัพธ์ทั้งหมดในรายการเดียว แสดงใน Haskell
return :: a -> [a]
return x = [x]
(<=<) :: (b -> [c]) -> (a -> [b]) -> (a -> [c])
g (<=<) f = concat . map g . f
หรือใช้ >>=
(>>=) :: [a] -> (a -> [b]) -> [b]
x >>= f = concat (map f x)
โปรดทราบว่าในตัวอย่างนี้ประเภทผลตอบแทนได้ดังนั้นมันจึงเป็นไปได้ว่าพวกเขาไม่ได้มีค่าของชนิดใด[a] ๆ aแน่นอนไม่มีข้อกำหนดดังกล่าวสำหรับ monad ที่ประเภทการคืนควรมีค่าเช่นนั้น monads บางคนมักจะมี (ชอบIOหรือState) แต่บางคนไม่เหมือนหรือ[]Maybe
IO monad
อย่างที่ฉันได้กล่าวไปแล้วว่าIOMonad นั้นค่อนข้างพิเศษ มูลค่าของประเภทIO aหมายถึงค่าของประเภทที่aสร้างขึ้นโดยการโต้ตอบกับสภาพแวดล้อมของโปรแกรม ดังนั้น (ต่างจากพระอื่น ๆ ทั้งหมด) เราไม่สามารถอธิบายคุณค่าของประเภทIO aโดยใช้การก่อสร้างที่บริสุทธิ์ นี่IOเป็นเพียงแท็กหรือป้ายกำกับที่แยกการคำนวณที่โต้ตอบกับสภาพแวดล้อม นี่คือ (กรณีเดียว) ที่มุมมอง # 1 และ # 2 ถูกต้อง
สำหรับIOmonad:
- องค์ประกอบ
f :: a -> IO bและg :: b -> IO cวิธีการ: คำนวณfที่มีปฏิสัมพันธ์กับสภาพแวดล้อมและจากนั้นคำนวณgที่ใช้ค่าและคำนวณผลลัพธ์ที่โต้ตอบกับสภาพแวดล้อม
returnเพียงเพิ่มIO"แท็ก" ให้กับค่า (เราเพียง "คำนวณ" ผลลัพธ์โดยการรักษาสภาพแวดล้อมให้เหมือนเดิม)
- กฎหมาย monad (การเชื่อมโยงตัวตน) ได้รับการรับรองโดยคอมไพเลอร์
หมายเหตุบางส่วน:
- เนื่องจากการคำนวณแบบ monadic มีประเภทผลลัพธ์เสมอดังนั้น
m aจึงไม่มีวิธี "หลบหนี" จากIOmonad ความหมายคือ: เมื่อการคำนวณโต้ตอบกับสภาพแวดล้อมคุณไม่สามารถสร้างการคำนวณจากสิ่งนั้นได้
- เมื่อโปรแกรมเมอร์ที่ใช้งานได้ไม่รู้ว่าจะทำบางสิ่งด้วยวิธีที่บริสุทธิ์ได้อย่างไรเขาสามารถ (เป็นทางเลือกสุดท้าย ) เขียนโปรแกรมโดยการคำนวณที่เป็นรัฐภายใน
IOMonad นี่คือเหตุผลที่IOมักจะเรียกว่าโปรแกรมเมอร์bin บาป
- ขอให้สังเกตว่าในโลกที่ไม่บริสุทธิ์ (ในแง่ของการเขียนโปรแกรมเชิงฟังก์ชัน) การอ่านค่าสามารถเปลี่ยนแปลงสภาพแวดล้อมได้เช่นกัน (เช่นใช้อินพุตของผู้ใช้) นั่นเป็นเหตุผลที่ฟังก์ชั่นเช่นต้องมีประเภทผลมาจากการ
getCharIO something