মনে করুন কোনও ফাংশনের পার্শ্ব প্রতিক্রিয়া রয়েছে। আমরা যদি ইনপুট এবং আউটপুট প্যারামিটার হিসাবে উত্পন্ন সমস্ত প্রভাব গ্রহণ করি, তবে ফাংশনটি বাইরের বিশ্বের কাছে খাঁটি।
সুতরাং, একটি অপরিষ্কার ফাংশন জন্য
f' :: Int -> Int
আমরা বিবেচনাতে রিয়েল ওয়ার্ল্ড যুক্ত করি
f :: Int -> RealWorld -> (Int, RealWorld)
-- input some states of the whole world,
-- modify the whole world because of the side effects,
-- then return the new world.
তারপর f
আবার খাঁটি। আমরা একটি প্যারামিট্রাইজড ডেটা টাইপ সংজ্ঞায়িত করি type IO a = RealWorld -> (a, RealWorld)
, সুতরাং আমাদের এতবার রিয়েলওয়ার্ড টাইপ করার দরকার নেই, এবং কেবল লিখতে পারি
f :: Int -> IO Int
প্রোগ্রামারটির কাছে, রিয়েল ওয়ার্ল্ডকে সরাসরি পরিচালনা করা খুব বিপজ্জনক particular বিশেষত, যদি কোনও প্রোগ্রামার রিয়েল ওয়ার্ল্ড টাইপের কোনও মূল্যতে হাত পান, তারা এটি অনুলিপি করার চেষ্টা করতে পারেন যা মূলত অসম্ভব। (উদাহরণস্বরূপ পুরো ফাইল সিস্টেমটি অনুলিপি করার চেষ্টা করার কথা ভাবুন you আপনি এটি কোথায় রাখবেন?) সুতরাং, আইওয়ের আমাদের সংজ্ঞা সমগ্র বিশ্বের রাজ্যগুলিকেও আবদ্ধ করে।
"অপরিষ্কার" ফাংশনগুলির সংমিশ্রণ
যদি আমরা তাদের একসাথে শৃঙ্খলা না করতে পারি তবে এই অশুচি ফাংশনগুলি অকেজো। বিবেচনা
getLine :: IO String ~ RealWorld -> (String, RealWorld)
getContents :: String -> IO String ~ String -> RealWorld -> (String, RealWorld)
putStrLn :: String -> IO () ~ String -> RealWorld -> ((), RealWorld)
আমরা চাই
- পেতে কনসোল থেকে একটি ফাইল,
- পড়া যে ফাইল, এবং
- কনসোলে ফাইলটির বিষয়বস্তু মুদ্রণ করুন।
আমরা যদি সত্যিকারের বিশ্বের রাষ্ট্রগুলিতে অ্যাক্সেস করতে পারি তবে কীভাবে করব?
printFile :: RealWorld -> ((), RealWorld)
printFile world0 = let (filename, world1) = getLine world0
(contents, world2) = (getContents filename) world1
in (putStrLn contents) world2 -- results in ((), world3)
আমরা এখানে একটি প্যাটার্ন দেখতে পাই। ফাংশনগুলিকে এভাবে বলা হয়:
...
(<result-of-f>, worldY) = f worldX
(<result-of-g>, worldZ) = g <result-of-f> worldY
...
সুতরাং আমরা কোনও অপারেটরকে ~~~
তাদের আবদ্ধ করতে সংজ্ঞায়িত করতে পারি :
(~~~) :: (IO b) -> (b -> IO c) -> IO c
(~~~) :: (RealWorld -> (b, RealWorld))
-> (b -> RealWorld -> (c, RealWorld))
-> (RealWorld -> (c, RealWorld))
(f ~~~ g) worldX = let (resF, worldY) = f worldX
in g resF worldY
তাহলে আমরা সহজভাবে লিখতে পারতাম
printFile = getLine ~~~ getContents ~~~ putStrLn
বাস্তব বিশ্বের স্পর্শ ছাড়া।
"Impurification"
এখন ধরা যাক আমরা ফাইলের সামগ্রীটিও বড় হাতের অক্ষরে তৈরি করতে চাই। বড় হাতছাড়া করা একটি খাঁটি ফাংশন
upperCase :: String -> String
তবে এটিকে বাস্তব বিশ্বে পরিণত করতে, এটির একটি ফিরে আসতে হবে IO String
। এই জাতীয় ফাংশন উত্তোলন করা সহজ:
impureUpperCase :: String -> RealWorld -> (String, RealWorld)
impureUpperCase str world = (upperCase str, world)
এটি সাধারণীকরণ করা যায়:
impurify :: a -> IO a
impurify :: a -> RealWorld -> (a, RealWorld)
impurify a world = (a, world)
যাতে impureUpperCase = impurify . upperCase
, এবং আমরা লিখতে পারি
printUpperCaseFile =
getLine ~~~ getContents ~~~ (impurify . upperCase) ~~~ putStrLn
(দ্রষ্টব্য: সাধারণত আমরা লিখি getLine ~~~ getContents ~~~ (putStrLn . upperCase)
)
আমরা সব সাথে monads সঙ্গে কাজ করছিলাম
এখন দেখা যাক আমরা কী করেছি:
- আমরা এমন একটি অপারেটর সংজ্ঞায়িত করেছি
(~~~) :: IO b -> (b -> IO c) -> IO c
যা দুটি অপরিষ্কার ক্রিয়াকলাপ এক সাথে জড়িত
- আমরা একটি ফাংশন সংজ্ঞায়িত করেছি
impurify :: a -> IO a
যা একটি খাঁটি মানকে অপবিত্রে রূপান্তর করে।
এখন আমরা সনাক্তকরণ (>>=) = (~~~)
এবং return = impurify
, এবং দেখুন? আমরা একটি monad পেয়েছি।
প্রযুক্তিগত নোট
এটি সত্যই একটি monad তা নিশ্চিত করার জন্য, এখনও কয়েকটি অক্ষ আছে যা খুব পরীক্ষা করা দরকার:
return a >>= f = f a
impurify a = (\world -> (a, world))
(impurify a ~~~ f) worldX = let (resF, worldY) = (\world -> (a, world )) worldX
in f resF worldY
= let (resF, worldY) = (a, worldX)
in f resF worldY
= f a worldX
f >>= return = f
(f ~~~ impurify) worldX = let (resF, worldY) = f worldX
in impurify resF worldY
= let (resF, worldY) = f worldX
in (resF, worldY)
= f worldX
f >>= (\x -> g x >>= h) = (f >>= g) >>= h
ব্যায়াম হিসাবে বাম।