ฉันจะบอกว่าสิ่งที่ดีที่สุดที่จะถามไม่ใช่วิธีที่เราจะเรียกมันว่า แต่วิธีที่เราจะวิเคราะห์ชิ้นส่วนของรหัสดังกล่าว และคำถามสำคัญแรกของฉันในการวิเคราะห์เช่นนี้จะเป็น:
- ผลข้างเคียงขึ้นอยู่กับอาร์กิวเมนต์ของฟังก์ชันหรือผลลัพธ์ที่เกิดจากผลข้างเคียงหรือไม่?
- ไม่: "ฟังก์ชั่น effectful" สามารถ refactored เป็นฟังก์ชั่นบริสุทธิ์การกระทำที่มีประสิทธิภาพและกลไกสำหรับการรวมพวกเขา
- ใช่: "ฟังก์ชั่น effectful" เป็นฟังก์ชั่นที่สร้างผลลัพธ์แบบ monadic
ตรงไปตรงมาเพื่อแสดงใน Haskell (และประโยคนี้เป็นเรื่องตลกเพียงครึ่งเดียว) ตัวอย่างของกรณี "ไม่" จะเป็นดังนี้:
double :: Num a => a -> IO a
double x = do
putStrLn "I'm doubling some number"
return (x*2)
ในตัวอย่างนี้การกระทำที่เราดำเนินการ (พิมพ์บรรทัด"I'm doubling some number"
) ไม่มีผลกระทบต่อความสัมพันธ์ระหว่างx
และผลลัพธ์ นี่หมายความว่าเราสามารถ refactor ด้วยวิธีนี้ (โดยใช้Applicative
คลาสและ*>
โอเปอเรเตอร์) ซึ่งแสดงให้เห็นว่าฟังก์ชันและเอฟเฟกต์เป็นจริง orthogonal:
double :: Num a => a -> IO a
double x = action *> pure (function x)
where
-- The pure function
function x = x*2
-- The side effect
action = putStrLn "I'm doubling some number"
ดังนั้นในกรณีนี้ฉันโดยส่วนตัวจะบอกว่ามันเป็นกรณีที่คุณสามารถแยกการทำงานที่บริสุทธิ์ การเขียนโปรแกรม Haskell มากมายเกี่ยวกับสิ่งนี้ - การเรียนรู้วิธีแยกส่วนที่บริสุทธิ์ออกจากโค้ดที่มีประสิทธิภาพ
ตัวอย่างของการเรียงลำดับ "ใช่" โดยที่ส่วนที่บริสุทธิ์และมีประสิทธิภาพไม่ได้เป็นฉากฉาก:
double :: Num a => a -> IO a
double x = do
putStrLn ("I'm doubling the number " ++ show x)
return (x*2)
x
ตอนนี้สตริงที่คุณพิมพ์ขึ้นอยู่กับมูลค่าของ อย่างไรก็ตามส่วนของฟังก์ชั่น (คูณx
สอง) ไม่ได้ขึ้นอยู่กับเอฟเฟกต์ทั้งหมดดังนั้นเราจึงยังสามารถแยกมันออกมาได้:
logged :: (a -> b) -> (a -> IO x) -> IO b
logged function logger a = do
logger a
return (function a)
double x = logged function logger
where function = (*2)
logger x putStrLn ("I'm doubling the number " ++ show x)
ฉันสามารถสะกดคำจากตัวอย่างอื่น ๆ ได้ แต่ฉันหวังว่านี่จะเพียงพอที่จะแสดงให้เห็นถึงจุดที่ฉันเริ่มต้นด้วย: คุณไม่ได้ "เรียก" มันเป็นบางสิ่งคุณวิเคราะห์ว่าชิ้นส่วนที่บริสุทธิ์และมีประสิทธิภาพเกี่ยวข้องกันอย่างไร เพื่อประโยชน์ของคุณ
นี่คือหนึ่งในเหตุผลที่ Haskell ใช้Monad
คลาสนี้อย่างกว้างขวาง Monads เป็นเครื่องมือในการวิเคราะห์และจัดโครงสร้างใหม่