আমার নম্র মতে বিখ্যাত প্রশ্নের উত্তর "একটি মোনাড কী?" বিশেষত সর্বাধিক ভোট প্রাপ্ত ব্যক্তিরা কেন মনোডের প্রয়োজনীয়তা রয়েছে তা স্পষ্ট করে না জানিয়ে মোনাদ কী তা বোঝানোর চেষ্টা করুন । এগুলি কি কোনও সমস্যার সমাধান হিসাবে ব্যাখ্যা করা যেতে পারে?
আমার নম্র মতে বিখ্যাত প্রশ্নের উত্তর "একটি মোনাড কী?" বিশেষত সর্বাধিক ভোট প্রাপ্ত ব্যক্তিরা কেন মনোডের প্রয়োজনীয়তা রয়েছে তা স্পষ্ট করে না জানিয়ে মোনাদ কী তা বোঝানোর চেষ্টা করুন । এগুলি কি কোনও সমস্যার সমাধান হিসাবে ব্যাখ্যা করা যেতে পারে?
উত্তর:
তারপরে, আমাদের প্রথম বড় সমস্যা রয়েছে। এটি একটি প্রোগ্রাম:
f(x) = 2 * x
g(x,y) = x / y
প্রথমে মৃত্যুদন্ড কার্যকর করার বিষয়টি কীভাবে আমরা বলতে পারি ? কীভাবে আমরা ফাংশনগুলির চেয়ে বেশি ব্যবহার না করে ফাংশনগুলির (যেমন একটি প্রোগ্রাম ) একটি আদেশযুক্ত ক্রম গঠন করতে পারি ?
সমাধান: রচনা ফাংশন । আপনি যদি প্রথমে চান g
এবং তারপর f
, শুধু লিখুন f(g(x,y))
। এই ভাবে, "প্রোগ্রাম" এর একটি ফাংশন হিসাবে ভাল: main = f(g(x,y))
। ঠিক আছে কিন্তু ...
আরও সমস্যা: কিছু ফাংশন ব্যর্থ হতে পারে (যেমন g(2,0)
, 0 দ্বারা ভাগ)। আমরা আশা করি আপনি না "ব্যতিক্রম" FP মধ্যে (একটি ব্যতিক্রম কোন ফাংশন নয়)। আমরা কীভাবে এটি সমাধান করব?
সমাধান: আসুন ফাংশনগুলিকে দুটি ধরণের জিনিস ফিরিয়ে আনতে দেওয়া যাক: g : Real,Real -> Real
দুটি ক্ষেত্র থেকে দুটি বাস্তবের মধ্যে ফাংশন রাখার পরিবর্তে আসুন g : Real,Real -> Real | Nothing
(দুটি বাস্তব থেকে (বাস্তব বা কিছুই নয়) ফাংশনটি ) অনুমতি দিন ।
তবে ফাংশনগুলি (সহজ হতে) কেবল একটি জিনিস ফেরত দেওয়া উচিত ।
সমাধান: আসুন ফেরত দেওয়ার জন্য একটি নতুন ধরণের ডেটা তৈরি করুন, একটি " বক্সিং টাইপ " যা সম্ভবত একটি বাস্তবকে আবদ্ধ করে দেয় বা কেবল কিছুই নয়। সুতরাং, আমরা থাকতে পারে g : Real,Real -> Maybe Real
। ঠিক আছে কিন্তু ...
এখন কি হবে f(g(x,y))
? f
একটি গ্রাস করতে প্রস্তুত নয় Maybe Real
। এবং, আমরা প্রতি ফাংশন আমরা এর সাথে যুক্ত হতে পারে পরিবর্তন করতে চাই না g
একটি গ্রাস Maybe Real
।
সমাধান: আসুন "সংযুক্ত" / "রচনা" / "লিঙ্ক" ফাংশনগুলির জন্য একটি বিশেষ ক্রিয়া করি । এইভাবে, আমরা, পর্দার আড়ালে, একটি ফাংশন আউটপুট নীচের একটি খাওয়ানোর জন্য অভিযোজিত করতে পারেন।
আমাদের ক্ষেত্রে ইন: g >>= f
(কানেক্ট / রচনা g
করতে f
)। আমরা আউটপুট >>=
পেতে চাই g
, এটি পরিদর্শন করতে চাই এবং যদি এটি Nothing
কেবল কল না করে f
ফিরে আসে Nothing
; বা বিপরীতে, বাক্সটি বের করুন Real
এবং f
এটি দিয়ে ফিড দিন। (এই অ্যালগরিদম কেবল প্রকারের >>=
জন্য বাস্তবায়ন Maybe
)। এছাড়াও নোট করুন যে প্রতি "বক্সিং টাইপ" (ভিন্ন বাক্স, বিভিন্ন অ্যাডাপ্টিং অ্যালগরিদম) প্রতি একবারই>>=
লিখতে হবে ।
অন্যান্য অনেক সমস্যা দেখা দেয় যা একই প্যাটার্নটি ব্যবহার করে সমাধান করা যেতে পারে: ১. বিভিন্ন অর্থ / মানকে কোডিফ / সংরক্ষণ করতে একটি "বাক্স" ব্যবহার করুন এবং g
সেই "বাক্সযুক্ত মানগুলি" ফিরিয়ে দেওয়ার মতো ফাংশন রাখুন । ২. এর আউটপুটটির ইনপুটটিতে g >>= f
সংযোগে সহায়তা করার জন্য কোনও সুরকার / লিঙ্কার রাখুন , সুতরাং আমাদের কোনও পরিবর্তন করতে হবে না ।g
f
f
এই কৌশলটি ব্যবহার করে সমাধানযোগ্য সমস্যাগুলি হ'ল:
একটি বিশ্বব্যাপী অবস্থা রয়েছে যে ক্রমানুসারে প্রতিটি ক্রিয়াকলাপ ("প্রোগ্রাম") ভাগ করতে পারে: সমাধান StateMonad
।
আমরা "অপরিষ্কার ফাংশন" পছন্দ করি না: ফাংশনগুলি যা একই ইনপুটটির জন্য আলাদা আউটপুট দেয় । সুতরাং, আসুন সেই ফাংশনগুলি চিহ্নিত করুন, যাতে তারা ট্যাগ / বাক্সযুক্ত মানটি ফিরিয়ে দেয়: মোনাড।IO
মোট সুখ!
IO
মনোড তালিকার আরও একটি সমস্যা IO
(point দফা)। অন্যদিকে IO
কেবল একবার এবং শেষে উপস্থিত হয়, সুতরাং, আপনার "বেশিরভাগ সময় কথা বলা ... আইও সম্পর্কে" বোঝেন না।
Either
)। সর্বাধিক উত্তরটি "আমাদের ফান্ট্যাক্টরগুলির দরকার কেন?" সম্পর্কে।
g >>= f
সংযোগ স্থাপনে সহায়তা করার জন্য একটি সুরকার / লিঙ্কার আছে , সুতরাং আমাদের কোনও পরিবর্তন করতে হবে না ।" g
f
f
এটি মোটেই ঠিক নয়। আগে, মধ্যে f(g(x,y))
, f
কিছু উত্পাদন করতে পারে। এটা হতে পারে f:: Real -> String
। "মোনাডিক রচনা" দিয়ে এটি উত্পাদন করতে অবশ্যই পরিবর্তন করা উচিতMaybe String
, অন্যথায় প্রকারগুলি মাপসই হয় না। তাছাড়া >>=
নিজেও ফিট করে না !! এটা >=>
যে, এই রচনা করে না >>=
। কার্লের উত্তরের অধীনে ডিফিউয়ারের সাথে আলোচনাটি দেখুন।
উত্তরটি অবশ্যই, "আমরা না" । সমস্ত বিমূর্ততা হিসাবে, এটি প্রয়োজন হয় না।
হাস্কেলের কোনও মোনাড বিমূর্তনের প্রয়োজন নেই। খাঁটি ভাষায় আইও করার জন্য এটি প্রয়োজনীয় নয়। IO
টাইপ নিজে যে শুধু জরিমানা যত্ন নেয়। বিদ্যমান কীটাণুজাতীয় desugaring do
ব্লক করার desugaring দিয়ে প্রতিস্থাপিত করা যেতে পারে bindIO
, returnIO
এবং failIO
হিসাবে সংজ্ঞায়িত GHC.Base
মডিউল। (এটি হ্যাকেজ সম্পর্কিত কোনও নথিভুক্ত মডিউল নয়, সুতরাং ডকুমেন্টেশনের জন্য আমাকে এর উত্সে নির্দেশ করতে হবে )) সুতরাং না, মোনাড বিমূর্তনের কোনও প্রয়োজন নেই।
সুতরাং এটির প্রয়োজন না হলে কেন এটি বিদ্যমান? কারণ এটি পাওয়া গিয়েছিল যে গণনার অনেকগুলি নিদর্শনগুলি monadic কাঠামো গঠন করে। একটি কাঠামোর বিমূর্ততা কোডটি লেখার পক্ষে অনুমতি দেয় যা সেই কাঠামোর সমস্ত দৃষ্টান্ত জুড়ে কাজ করে। এটি আরও সংক্ষিপ্তভাবে বলতে - কোড পুনরায় ব্যবহার।
কার্যকরী ভাষায়, কোড পুনরায় ব্যবহারের জন্য সর্বাধিক শক্তিশালী সরঞ্জামটি হ'ল ফাংশনগুলির রচনা। ভাল পুরানো (.) :: (b -> c) -> (a -> b) -> (a -> c)
অপারেটর অত্যন্ত ক্ষমতাবান। এটি ছোট ফাংশনগুলি লিখতে এবং ন্যূনতম সিনট্যাকটিক বা শব্দার্থক ওভারহেডের সাথে একসাথে আঠালো করে তোলে।
তবে এমন কিছু মামলা রয়েছে যখন প্রকারগুলি বেশ সঠিকভাবে কাজ করে না। আপনি যখন করবেন foo :: (b -> Maybe c)
এবং কি করবেন bar :: (a -> Maybe b)
? foo . bar
টাইপচেক করে না, কারণ b
এবং Maybe b
একই ধরণের নয়।
তবে ... এটা প্রায় সঠিক। আপনি শুধু কিছুটা অবকাশ চান। আপনি চিকিত্সা করতে সক্ষম হতে চান Maybe b
এটি মূলত ছিল b
। যদিও তাদেরকে একই ধরণের হিসাবে চুপচাপ আউট করা খুব খারাপ ধারণা। এটি কমবেশি নাল পয়েন্টারগুলির মতো একই জিনিস, যা টনি হোয়ের বিখ্যাতভাবে বিলিয়ন ডলারের ভুল বলে অভিহিত করেছে । সুতরাং যদি আপনি তাদের একই ধরণের হিসাবে আচরণ করতে না পারেন, তবে আপনি রচনা ব্যবস্থার সরবরাহিত প্রসারকে বাড়ানোর কোনও উপায় খুঁজে পেতে পারেন (.)
।
সেক্ষেত্রে অন্তর্নিহিত তত্ত্বটি সত্যই পরীক্ষা করা গুরুত্বপূর্ণ (.)
। ভাগ্যক্রমে, কেউ ইতিমধ্যে আমাদের জন্য এটি করেছে। এটা পরিনত হয় যে সমন্বয় (.)
এবং id
একটি গাণিতিক কনস্ট্রাক্ট একটি নামে পরিচিত গঠন বিভাগ । বিভাগগুলি গঠনের অন্যান্য উপায়ও রয়েছে। উদাহরণস্বরূপ একটি ক্লাইসলি বিভাগটি অবজেক্টগুলিকে কিছুটা বাড়িয়ে তোলার অনুমতি দেয়। জন্য একটি Kleisli বিভাগ Maybe
গঠিত হবে (.) :: (b -> Maybe c) -> (a -> Maybe b) -> (a -> Maybe c)
এবং id :: a -> Maybe a
। এটি হল, বিভাগে থাকা অবজেক্টগুলি (->)
ক এর সাথে বৃদ্ধি করে Maybe
, তাই (a -> b)
হয়ে যায় (a -> Maybe b)
।
এবং হঠাৎ করে, আমরা এমন কিছুতে রচনার শক্তি প্রসারিত করেছি যা traditionalতিহ্যবাহী (.)
অপারেশনটি কাজ করে না। এটি নতুন বিমূর্ত শক্তি পাওয়ার উত্স। ক্লাইসলি বিভাগগুলি কেবল ধরণের চেয়ে আরও বেশি ধরণের কাজ করে Maybe
। তারা বিভাগের আইন মেনে একটি যথাযথ বিভাগ একত্র করতে পারে এমন প্রতিটি ধরণের সাথে কাজ করে।
id . f
=f
f . id
=f
f . (g . h)
=(f . g) . h
যতক্ষণ আপনি প্রমাণ করতে পারেন যে আপনার ধরণটি এই তিনটি আইন মেনে চলে, আপনি এটিকে ক্লাইসলি বিভাগে পরিণত করতে পারেন। আর এ নিয়ে বড় কথা কী? ঠিক আছে, দেখা গেছে যে মনাদগুলি ক্লাইসলি বিভাগগুলির মতো একই জিনিস। Monad
এর return
ক্লাইসলির মতোই id
। Monad
এর (>>=)
Kleisli অভিন্ন নয় (.)
, কিন্তু এটি সক্রিয় আউট অন্যান্য পদ খুব সহজ প্রতিটি লিখতে হবে। এবং বিভাগ আইন একসংখ্যা আইন, যখন আপনি তাদের মধ্যে পার্থক্য জুড়ে অনুবাদ মতই (>>=)
এবং (.)
।
তাহলে এত ঝামেলা করে কেন? Monad
ভাষাতে কেন বিমূর্ততা রয়েছে? আমি উপরে বর্ণিত হিসাবে এটি কোড পুনরায় ব্যবহার সক্ষম করে। এমনকি কোডটি পুনরায় ব্যবহারকে দুটি ভিন্ন মাত্রা সহ সক্ষম করে।
কোড পুনরায় ব্যবহারের প্রথম মাত্রা সরাসরি বিমূর্ততার উপস্থিতি থেকে আসে। আপনি কোডটি লিখতে পারেন যা বিমূর্ততার সমস্ত দৃষ্টান্ত জুড়ে কাজ করে। পুরো মোনাড-লুপস প্যাকেজটিতে লুপগুলি রয়েছে যা কোনও উদাহরণ সহ কাজ করে Monad
।
দ্বিতীয় মাত্রা অপ্রত্যক্ষ, কিন্তু এটি রচনাটির অস্তিত্ব থেকে অনুসরণ করে। যখন রচনাটি সহজ হয়, ছোট, পুনরায় ব্যবহারযোগ্য অংশগুলিতে কোড লেখা স্বাভাবিক। এটি একই পদ্ধতিতে (.)
অপারেটরকে ছোট, পুনরায় ব্যবহারযোগ্য ফাংশনগুলি লেখার জন্য উত্সাহ দেয়।
তাহলে বিমূর্ততা কেন বিদ্যমান? কারণ এটি এমন একটি সরঞ্জাম হিসাবে প্রমাণিত হয়েছে যা কোডে আরও রচনা তৈরি করতে সক্ষম করে, যার ফলে পুনরায় ব্যবহারযোগ্য কোড তৈরি করা যায় এবং আরও পুনরায় ব্যবহারযোগ্য কোড তৈরি করতে উত্সাহিত করা হয়। কোড পুনর্ব্যবহার প্রোগ্রামিংয়ের অন্যতম পবিত্র গ্রিল। মোনাড বিমূর্ততা বিদ্যমান কারণ এটি আমাদের সেই পবিত্র গ্রেিলের দিকে একটু এগিয়ে নিয়ে যায়।
newtype Kleisli m a b = Kleisli (a -> m b)
,। ক্লাইসলি বিভাগগুলি এমন ফাংশন যেখানে শ্রেণীবদ্ধ রিটার্ন টাইপ ( b
এই ক্ষেত্রে) কোনও টাইপ নির্মাণকারীর পক্ষে যুক্তি m
। আইএফ Kleisli m
একটি বিভাগ গঠন করে, m
এটি একটি মোনাড।
Kleisli m
একটি বিভাগ যার যে বস্তু থেকে তীর Haskell, ধরনের এবং এই ধরনের হয় গঠন বলে মনে হয় a
করার b
থেকে ফাংশন হয় a
করার m b
সঙ্গে, id = return
এবং (.) = (<=<)
। এটি কি সঠিক, বা আমি বিভিন্ন স্তরের জিনিস বা কোনও কিছু মিশ্রিত করছি?
a
এবং b
তবে সেগুলি সাধারণ ফাংশন নয়। m
ফাংশনের রিটার্ন ভ্যালুতে তারা অতিরিক্ত দিয়ে সজ্জিত ।
বেনজামিন পিয়ার্স টিএপিএলে ড
কোনও প্রোগ্রামে শর্তাদির রান-টাইম আচরণগুলির জন্য এক ধরণের স্থিতিশীল আনুমানিক গণনা হিসাবে একটি ধরণের সিস্টেমকে বিবেচনা করা যেতে পারে।
একারণে শক্তিশালী টাইপ সিস্টেমে সজ্জিত কোনও ভাষা স্বল্প টাইপিত ভাষার চেয়ে কঠোরভাবে উদ্বেগজনক। আপনি একইভাবে মনাদদের সম্পর্কে ভাবতে পারেন।
@ কার্ল এবং সিগফাই পয়েন্ট হিসাবে , আপনি মোনাড, টাইপক্লাস বা অন্য যে কোনও বিমূর্ত স্টাফের অবলম্বন না করে আপনি চান সমস্ত অপারেশন দিয়ে একটি ডেটাটাইপ সজ্জিত করতে পারেন। তবে ম্যানডস আপনাকে কেবল পুনরায় ব্যবহারযোগ্য কোডটি লেখার অনুমতি দেয় না, সমস্ত রিন্ডান্ট ডিটাইলগুলি বিমূর্ত করতে দেয়।
উদাহরণ হিসাবে, ধরুন আমরা একটি তালিকা ফিল্টার করতে চাই। সহজ উপায় হ'ল filter
ফাংশনটি ব্যবহার করা filter (> 3) [1..10]
:, যা সমান [4,5,6,7,8,9,10]
।
এর আরও কিছুটা জটিল সংস্করণ filter
, যা বাম থেকে ডানে একটি সঞ্চালককেও পাস করে
swap (x, y) = (y, x)
(.*) = (.) . (.)
filterAccum :: (a -> b -> (Bool, a)) -> a -> [b] -> [b]
filterAccum f a xs = [x | (x, True) <- zip xs $ snd $ mapAccumL (swap .* f) a xs]
সব পেতে i
, যেমন i <= 10, sum [1..i] > 4, sum [1..i] < 25
, আমরা লিখতে পারি
filterAccum (\a x -> let a' = a + x in (a' > 4 && a' < 25, a')) 0 [1..10]
যা সমান [3,4,5,6]
।
বা আমরা nub
ফাংশনটিকে নতুন করে সংজ্ঞায়িত করতে পারি , যা তালিকা থেকে সদৃশ উপাদানগুলিকে সরিয়ে দেয় filterAccum
:
nub' = filterAccum (\a x -> (x `notElem` a, x:a)) []
nub' [1,2,4,5,4,3,1,8,9,4]
সমান [1,2,4,5,3,8,9]
। এখানে একটি সংগ্রহকারী হিসাবে একটি তালিকা পাস করা হয়। কোডটি কাজ করে, কারণ তালিকা মোনাড ছেড়ে যাওয়া সম্ভব, সুতরাং সম্পূর্ণ গণনা খাঁটি থাকে ( আসলে notElem
ব্যবহার করে না >>=
, তবে এটি পারে)। তবে নিরাপদে আইও মোনাড ছেড়ে যাওয়া সম্ভব নয় (যেমন আপনি কোনও আইও ক্রিয়া চালাতে পারবেন না এবং খাঁটি মানটি ফিরিয়ে দিতে পারবেন না - মান সর্বদা আইও মোনাডে আবৃত থাকবে)। আর একটি উদাহরণ পার্সোনাল অ্যারেগুলি: আপনি এসটি মোনাড ছাড়ার পরে, যেখানে একটি পরিবর্তনীয় অ্যারে থাকেন, আপনি আর ধ্রুবক সময়ে অ্যারে আপডেট করতে পারবেন না। সুতরাং আমাদের Control.Monad
মডিউল থেকে একটি monadic ফিল্টারিং প্রয়োজন :
filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
filterM _ [] = return []
filterM p (x:xs) = do
flg <- p x
ys <- filterM p xs
return (if flg then x:ys else ys)
filterM
তালিকা থেকে সমস্ত উপাদানগুলির জন্য এক একাত্মক ক্রিয়াকলাপ কার্যকর করে, ফলনকারী উপাদানগুলি, যার জন্য মোনাডিক ক্রিয়া ফিরে আসে True
।
একটি অ্যারে সহ একটি ফিল্টারিং উদাহরণ:
nub' xs = runST $ do
arr <- newArray (1, 9) True :: ST s (STUArray s Int Bool)
let p i = readArray arr i <* writeArray arr i False
filterM p xs
main = print $ nub' [1,2,4,5,4,3,1,8,9,4]
[1,2,4,5,3,8,9]
প্রত্যাশিত হিসাবে মুদ্রণ ।
এবং আইও মোনাড সহ একটি সংস্করণ, যা কোন উপাদানগুলি ফিরে আসবে তা জিজ্ঞাসা করে:
main = filterM p [1,2,4,5] >>= print where
p i = putStrLn ("return " ++ show i ++ "?") *> readLn
যেমন
return 1? -- output
True -- input
return 2?
False
return 4?
False
return 5?
True
[1,5] -- output
এবং একটি চূড়ান্ত চিত্র হিসাবে filterAccum
শর্তাবলী সংজ্ঞায়িত করা যেতে পারে filterM
:
filterAccum f a xs = evalState (filterM (state . flip f) xs) a
সঙ্গে StateT
একসংখ্যা, যে ফণা অধীনে ব্যবহার করা হয়, শুধু একজন সাধারণ ডাটাটাইপ হচ্ছে।
এই উদাহরণটি উদাহরণস্বরূপ বোঝায় যে, মোনাডগুলি আপনাকে কেবল গণ্যসংক্রান্ত প্রসঙ্গটি বিমূর্ত করতে এবং পরিষ্কার পুনরায় ব্যবহারযোগ্য কোড লিখতে দেয় না (মনডের সংশ্লেষের কারণে, @ কার্ল ব্যাখ্যা করেছেন), তবে ব্যবহারকারী-সংজ্ঞায়িত ডেটাটাইপগুলি এবং অন্তর্নির্মিত আদিমকে অভিন্ন হিসাবে চিকিত্সা করার জন্য।
আমি মনে করি না যে IO
এটি একটি বিশেষভাবে অসামান্য মোনাড হিসাবে দেখা উচিত, তবে এটি অবশ্যই নবীনদের জন্য আরও চমকপ্রদ একটি, তাই আমি এটি আমার ব্যাখ্যার জন্য ব্যবহার করব।
খাঁটি-কার্যকরী ভাষার জন্য সবচেয়ে সহজ ধারণাযোগ্য আইও সিস্টেমটি (এবং বাস্তবে যে হাস্কেল দিয়ে শুরু হয়েছিল) এটি হ'ল:
main₀ :: String -> String
main₀ _ = "Hello World"
অলসতার সাথে, সেই সাধারণ স্বাক্ষরটি আসলে ইন্টারেক্টিভ টার্মিনাল প্রোগ্রামগুলি তৈরি করতে যথেষ্ট - যদিও খুব সীমাবদ্ধ। সবচেয়ে হতাশার বিষয় হ'ল আমরা কেবল পাঠ্য আউটপুট করতে পারি। যদি আমরা আরও কিছু উত্তেজনাপূর্ণ আউটপুট সম্ভাবনা যুক্ত করি?
data Output = TxtOutput String
| Beep Frequency
main₁ :: String -> [Output]
main₁ _ = [ TxtOutput "Hello World"
-- , Beep 440 -- for debugging
]
সুন্দর, তবে অবশ্যই অনেক বেশি বাস্তবসম্মত "পরিবর্তনের আউটপুট" কোনও ফাইলে লেখা হবে । তবে আপনি ফাইল থেকে পড়ার কিছু উপায়ও চাইবেন । কোন সুযোগ?
ঠিক আছে, যখন আমরা আমাদের main₁
প্রোগ্রামটি গ্রহণ করি এবং কেবল কোনও প্রক্রিয়াটি (অপারেটিং সিস্টেমের সুবিধা ব্যবহার করে) কোনও ফাইল পাইপ করি, তখন আমরা মূলত ফাইল-রিডিং বাস্তবায়ন করি। আমরা যদি হ্যাস্কেল ভাষার মধ্যে থেকে ফাইল-পঠন ট্রিগার করতে পারি ...
readFile :: Filepath -> (String -> [Output]) -> [Output]
এটি একটি "ইন্টারেক্টিভ প্রোগ্রাম" ব্যবহার করবে String->[Output]
, এটি কোনও ফাইল থেকে প্রাপ্ত স্ট্রিংটি খাওয়াবে এবং একটি ইন্টারঅ্যাক্টিভ প্রোগ্রাম দেবে যা প্রদত্তটিকে কেবল কার্যকর করে।
সেখানে এক সমস্যা এখানে: আমরা সত্যিই একটি ধারণা না থাকে যখন ফাইল পড়া হয়। [Output]
তালিকা নিশ্চিত একটা চমৎকার অর্ডার দেয় আউটপুট , কিন্তু আমরা জন্য একটা অর্ডার পাবেন না ইনপুট সম্পন্ন করা হবে।
সমাধান: ইনপুট-ইভেন্টগুলি করণীয় তালিকার আইটেম তৈরি করুন।
data IO₀ = TxtOut String
| TxtIn (String -> [Output])
| FileWrite FilePath String
| FileRead FilePath (String -> [Output])
| Beep Double
main₂ :: String -> [IO₀]
main₂ _ = [ FileRead "/dev/null" $ \_ ->
[TxtOutput "Hello World"]
]
ঠিক আছে, এখন আপনি একটি ভারসাম্যহীনতা চিহ্নিত করতে পারেন: আপনি একটি ফাইল পড়তে পারেন এবং আউটপুটকে এর উপর নির্ভরশীল করতে পারেন, তবে আপনি সিদ্ধান্ত নিতে ফাইলের সামগ্রীগুলি ব্যবহার করতে পারবেন না যেমন অন্য কোনও ফাইলও পড়তে পারেন। সুস্পষ্ট সমাধান: ইনপুট-ইভেন্টগুলির ফলাফলকেও IO
কেবল টাইপ কিছু নয় Output
। নিশ্চিত যে সাধারণ পাঠ্য আউটপুট অন্তর্ভুক্ত, কিন্তু অতিরিক্ত ফাইল ইত্যাদি পড়ার অনুমতি দেয়।
data IO₁ = TxtOut String
| TxtIn (String -> [IO₁])
| FileWrite FilePath String
| FileRead FilePath (String -> [IO₁])
| Beep Double
main₃ :: String -> [IO₁]
main₃ _ = [ TxtIn $ \_ ->
[TxtOut "Hello World"]
]
এটি এখন আপনাকে প্রোগ্রামে চাইলে যে কোনও ফাইল অপারেশন প্রকাশ করার অনুমতি দেবে (যদিও এটি ভাল পারফরম্যান্সের সাথে নয়) তবে এটি কিছুটা জটিলভাবে জটিল:
main₃
ক্রিয়াকলাপগুলির একটি সম্পূর্ণ তালিকা দেয়। কেন আমরা কেবল স্বাক্ষরটি ব্যবহার করি না :: IO₁
, যা এটি একটি বিশেষ ক্ষেত্রে হিসাবে রয়েছে?
তালিকাগুলি প্রকৃতপক্ষে প্রোগ্রাম প্রবাহের একটি নির্ভরযোগ্য ওভারভিউ দেয় না: বেশিরভাগ পরবর্তী গণনাগুলি কিছু ইনপুট ক্রিয়াকলাপের ফলাফল হিসাবে কেবল "ঘোষিত" হবে। সুতরাং আমরা পাশাপাশি তালিকা কাঠামো খাঁজতে পারে এবং প্রতিটি আউটপুট ক্রিয়াকলাপে কেবল "এবং তারপরে" করতে পারি।
data IO₂ = TxtOut String IO₂
| TxtIn (String -> IO₂)
| Terminate
main₄ :: IO₂
main₄ = TxtIn $ \_ ->
TxtOut "Hello World"
Terminate
এতোটা খারাপ না!
অনুশীলনে, আপনি আপনার সমস্ত প্রোগ্রাম সংজ্ঞায়িত করার জন্য সরল নির্মাণকারী ব্যবহার করতে চাইবেন না। এ জাতীয় মৌলিক নির্মাণকারীদের একটি ভাল দম্পতি থাকা দরকার, তবুও বেশিরভাগ উচ্চ-স্তরের স্টাফের জন্য আমরা কিছু সুন্দর উচ্চ-স্তরের স্বাক্ষর সহ একটি ফাংশন লিখতে চাই। এটি দেখা যায় যে এর বেশিরভাগটি একই রকম দেখাবে: কিছু ধরণের অর্থপূর্ণ-টাইপযুক্ত মান গ্রহণ করুন, এবং ফলাফল হিসাবে একটি আইও ক্রিয়া অর্জন করুন।
getTime :: (UTCTime -> IO₂) -> IO₂
randomRIO :: Random r => (r,r) -> (r -> IO₂) -> IO₂
findFile :: RegEx -> (Maybe FilePath -> IO₂) -> IO₂
স্পষ্টতই এখানে একটি নিদর্শন রয়েছে এবং আমরা এটি আরও ভাল লিখতে চাই
type IO₃ a = (a -> IO₂) -> IO₂ -- If this reminds you of continuation-passing
-- style, you're right.
getTime :: IO₃ UTCTime
randomRIO :: Random r => (r,r) -> IO₃ r
findFile :: RegEx -> IO₃ (Maybe FilePath)
এখন এটি পরিচিত দেখাতে শুরু করে, তবে আমরা এখনও কেবল হুডের নীচে পাতলা-ছদ্মবেশযুক্ত প্লেইন ফাংশনগুলি নিয়ে কাজ করছি, এবং এটি ঝুঁকিপূর্ণ: প্রতিটি "মান-অ্যাকশন" এর কোনও কার্যত কোনও কার্যকারিতার ফলাফলের ক্রিয়াটি পাস করার দায়িত্ব রয়েছে (অন্যথায়) পুরো প্রোগ্রামটির নিয়ন্ত্রণ প্রবাহটি মাঝখানে একটি খারাপ ব্যবহারের দ্বারা সহজেই ব্যাহত হয়)। আমরা আরও ভালভাবে এই প্রয়োজনটি সুস্পষ্ট করে তুলতে চাই। ঠিক আছে, এটি মোনাড আইনগুলি প্রমাণিত করে , যদিও আমি নিশ্চিত নই যে আমরা স্ট্যান্ডার্ড বাইন্ড / অপারেটরদের সাথে যুক্ত হওয়া ছাড়া সত্যই সেগুলি তৈরি করতে পারি।
যে কোনও হারে, আমরা এখন আইও-র একটি সূচনায় পৌঁছেছি যার যথাযথ মনড উদাহরণ রয়েছে:
data IO₄ a = TxtOut String (IO₄ a)
| TxtIn (String -> IO₄ a)
| TerminateWith a
txtOut :: String -> IO₄ ()
txtOut s = TxtOut s $ TerminateWith ()
txtIn :: IO₄ String
txtIn = TxtIn $ TerminateWith
instance Functor IO₄ where
fmap f (TerminateWith a) = TerminateWith $ f a
fmap f (TxtIn g) = TxtIn $ fmap f . g
fmap f (TxtOut s c) = TxtOut s $ fmap f c
instance Applicative IO₄ where
pure = TerminateWith
(<*>) = ap
instance Monad IO₄ where
TerminateWith x >>= f = f x
TxtOut s c >>= f = TxtOut s $ c >>= f
TxtIn g >>= f = TxtIn $ (>>=f) . g
স্পষ্টতই এটি আইও এর কার্যকর প্রয়োগ নয়, তবে এটি নীতিগতভাবে ব্যবহারযোগ্য।
IO3 a ≡ Cont IO2 a
। তবে আমি বোঝাতে চেয়েছিলাম যে যারা ইতিমধ্যে ধারাবাহিকতা মোনাদকে জানেন তাদের পক্ষে এই মন্তব্যটি আরও একটি সম্মতি হিসাবে জানানো হয়েছে, কারণ এটির সাথে ঠিক বান্ধব হিসাবে খ্যাতি নেই।
এক শ্রেণীর পুনরাবৃত্ত সমস্যাগুলি সমাধান করার জন্য মনডস কেবল একটি সুবিধাজনক কাঠামো। প্রথমত, monads হওয়া আবশ্যক functors (অর্থাত উপাদানের (বা তাদের টাইপ) দিকে না তাকিয়েই ম্যাপিং সমর্থন করতে হবে) তাহলে তাদের একটি আনতে হবে বাঁধাই (অথবা chaining) অপারেশন এবং একটি উপাদান টাইপ থেকে একটি পরমাণুসদৃশ্য মান (তৈরি করতে একটি উপায় return
)। শেষ অবধি, bind
এবং return
অবশ্যই দুটি সমীকরণ (বাম এবং ডান সনাক্তকরণ) সন্তুষ্ট করতে হবে, এটি মোনাড আইনও বলে। (বিকল্পভাবে কেউ flattening operation
বাঁধাইয়ের পরিবর্তে মনডকে সংজ্ঞায়িত করতে পারে ))
তালিকা একসংখ্যা সাধারণভাবে অ নিয়তিবাদ সঙ্গে মোকাবিলা করার জন্য ব্যবহার করা হয়। বাইন্ড অপারেশন তালিকার একটি উপাদান নির্বাচন করে (অন্তর্নিহিতভাবে তাদের সকলের সমান্তরাল দুনিয়াতে ) প্রোগ্রামারকে তাদের সাথে কিছু গণনা করতে দেয় এবং তারপরে সমস্ত বিশ্বের ফলাফলগুলিকে একক তালিকার সাথে সংমিশ্রণ করে (ক্যানটেটেটিং বা ফ্ল্যাটেন্টিং দ্বারা, একটি নেস্টেড তালিকার মাধ্যমে) )। এখানে কীভাবে একজন হাস্কেলের মোনাডিক কাঠামোয় কোনও ক্রম নির্ধারণের কার্যকারিতা সংজ্ঞায়িত করবেন:
perm [e] = [[e]]
perm l = do (leader, index) <- zip l [0 :: Int ..]
let shortened = take index l ++ drop (index + 1) l
trailer <- perm shortened
return (leader : trailer)
এখানে একটি উদাহরণ repl অধিবেশন:
*Main> perm "a"
["a"]
*Main> perm "ab"
["ab","ba"]
*Main> perm ""
[]
*Main> perm "abc"
["abc","acb","bac","bca","cab","cba"]
এটি লক্ষ করা উচিত যে তালিকা মোনাড কোনওভাবেই কোনও পার্শ্ব-প্রভাবের গণনা নেই। একটি গাণিতিক কাঠামো একটি মনাদ (অর্থাত্ উল্লিখিত ইন্টারফেস এবং আইন মেনে চলা) পার্শ্ব প্রতিক্রিয়া বোঝায় না, যদিও পার্শ্ব-প্রতিক্রিয়াশীল ঘটনাটি বেশিরভাগভাবে মোনাদিক কাঠামোর সাথে মাপসই হয়।
মনডগুলি মূলত একটি শৃঙ্খলে একসাথে ফাংশন রচনা করতে পরিবেশন করে। সময়কাল।
এখন তারা রচনা করার পদ্ধতিটি বিদ্যমান মনাদগুলিতে পার্থক্য করে, ফলে বিভিন্ন আচরণের ফলে (যেমন, রাজ্য মনাদে পরিবর্তনীয় অবস্থার অনুকরণ করা)।
স্নাতকদের সম্পর্কে বিভ্রান্তিটি হ'ল এত সাধারণ, অর্থাত্ কার্যকারিতা রচনা করার একটি পদ্ধতি, এগুলি অনেক কিছুর জন্য ব্যবহার করা যেতে পারে, ফলে লোকেরা বিশ্বাস করতে পারে যে মনডগুলি রাষ্ট্র সম্পর্কে, আইও সম্পর্কিত, ইত্যাদি, যখন তারা কেবল "রচনা কার্য" সম্পর্কে থাকে "।
এখন, মনাদাদের সম্পর্কে একটি আকর্ষণীয় বিষয় হ'ল রচনাটির ফলাফলটি সর্বদা "এম এ" টাইপ হয়, এটি একটি এমফের সাথে ট্যাগ করা একটি খামের অভ্যন্তরের একটি মান। এই বৈশিষ্ট্যটি বাস্তবায়িত করতে সত্যিই দুর্দান্ত বলে মনে হয়, উদাহরণস্বরূপ, অশুচি কোড থেকে খাঁটি মধ্যে স্পষ্ট বিভাজন: সমস্ত অপরিষ্কার ক্রিয়াকলাপ "IO a" টাইপ হিসাবে ঘোষণা করুন এবং আইও মোনাডকে সংজ্ঞায়িত করার সময় কোনও কার্য সরবরাহ করবেন না, " "আইও এ" এর ভিতরে থেকে একটি মান। ফলাফলটি হ'ল কোনও ফাংশন বিশুদ্ধ হতে পারে না এবং একই সাথে একটি "আইও এ" থেকে একটি মানও বের করে নিতে পারে, কারণ খাঁটি থাকার সময় এই জাতীয় মান নেওয়ার কোনও উপায় নেই (ফাংশনটি অবশ্যই "আইও" মনডের অভ্যন্তরে থাকতে হবে) যেমন মান)। (দ্রষ্টব্য: ভাল, কিছুই নিখুঁত নয়, তাই "IO straitjacket" কে "অনিরাপদ পারফর্মফর্মআইও: আইও এ -> এ" ব্যবহার করে ভাঙা যায়
আপনার যদি কোনও ধরণের কনস্ট্রাক্টর এবং ফাংশন থাকে যা সেই ধরণের পরিবারের মানগুলি ফিরিয়ে দেয় তবে আপনার স্নাতকের প্রয়োজন । অবশেষে, আপনি চাই একসঙ্গে ফাংশন এই ধরনের একত্রিত । কেন উত্তর দেওয়ার জন্য এই তিনটি মূল উপাদান ।
আমাকে বিস্তারিত জানাতে দিন। আপনার আছে Int
, String
এবং Real
টাইপ এর ফাংশন Int -> String
, String -> Real
ইত্যাদি। আপনি এই ফাংশনগুলি সহজেই একত্রিত করতে পারেন, শেষ করে Int -> Real
। জীবন সুন্দর.
তারপরে, একদিন আপনার নতুন ধরণের পরিবার তৈরি করতে হবে । এটি হতে পারে কারণ আপনার কোনও মূল্য ( Maybe
) প্রদান না করে, একটি ত্রুটি ( Either
), একাধিক ফলাফল ( List
) ফিরিয়ে দেওয়ার সম্ভাবনা বিবেচনা করতে হবে ।
লক্ষ্য করুন যে Maybe
এটি একটি টাইপ কনস্ট্রাক্টর। এটি একটি ধরণের লাগে, যেমন Int
এবং নতুন টাইপ দেয় Maybe Int
। প্রথম জিনিসটি মনে রাখবেন, কোনও টাইপ কনস্ট্রাক্টর নেই, কোনও মোনাদ নেই।
অবশ্যই, আপনি আপনার কোডে আপনার টাইপ কনস্ট্রাক্টর ব্যবহার করতে চান এবং শীঘ্রই আপনি Int -> Maybe String
এবং এর মতো ফাংশনগুলি দিয়ে শেষ করেন String -> Maybe Float
। এখন, আপনি সহজেই আপনার কার্যগুলি একত্রিত করতে পারবেন না। জীবন আর ভাল হয় না।
মনডা উদ্ধারে আসার সময় এখানে। তারা আপনাকে আবার এই জাতীয় ফাংশন একত্রিত করার অনুমতি দেয়। আপনার কেবল রচনাটি পরিবর্তন করা দরকার । জন্য > == ।