আমাদের কেন সোনার দরকার?


366

আমার নম্র মতে বিখ্যাত প্রশ্নের উত্তর "একটি মোনাড কী?" বিশেষত সর্বাধিক ভোট প্রাপ্ত ব্যক্তিরা কেন মনোডের প্রয়োজনীয়তা রয়েছে তা স্পষ্ট করে না জানিয়ে মোনাদ কী তা বোঝানোর চেষ্টা করুন । এগুলি কি কোনও সমস্যার সমাধান হিসাবে ব্যাখ্যা করা যেতে পারে?




4
আপনি ইতিমধ্যে কি গবেষণা করেছেন? আপনি কোথায় তাকিয়ে আছে? আপনি কি সংস্থান খুঁজে পেয়েছেন? আমরা আপনাকে জিজ্ঞাসার আগে একটি উল্লেখযোগ্য পরিমাণে গবেষণা করার প্রত্যাশা করি এবং আপনি কী গবেষণা করেছেন তা প্রশ্নে আমাদের দেখান । এমন অনেক সংস্থান আছে যা সংস্থানগুলির অনুপ্রেরণা ব্যাখ্যা করার চেষ্টা করে - যদি আপনি কিছু খুঁজে না পান তবে আপনার আরও কিছু গবেষণা করার প্রয়োজন হতে পারে। আপনি যদি কিছু খুঁজে পেয়েছেন তবে তারা আপনাকে সহায়তা না করে, আপনি কী খুঁজে পেয়েছেন এবং কেন বিশেষভাবে তারা আপনার জন্য কাজ করেনি তা ব্যাখ্যা করে যদি এটি আপনাকে আরও ভাল প্রশ্ন করে তোলে।
DW

8
এটি অবশ্যই প্রোগ্রামার্স.স্ট্যাকএক্সচেঞ্জের জন্য ভাল ফিট এবং স্ট্যাকওভারফ্লোয়ের জন্য ভাল ফিট নয়। আমি পারলে মাইগ্রেটে ভোট দেব, তবে পারব না। = (
jpmc26

3
@ jpmc26 সম্ভবত এটি "প্রাথমিকভাবে মতামত ভিত্তিক" হিসাবে বন্ধ হয়ে যাবে; এখানে এটি কমপক্ষে একটি সুযোগ দাঁড়িয়েছে (যেমন বিপুল সংখ্যক উপবিষ্টের দ্বারা দেখানো হয়েছে, গতকাল দ্রুত পুনরায় খোলা হয়েছে, এবং এখনও নিকটতম ভোট নেই)
ইজকাটা

উত্তর:


580

আমাদের কেন সোনার দরকার?

  1. আমরা কেবল ফাংশন ব্যবহার করে প্রোগ্রাম করতে চাই । (সর্বোপরি "ফাংশনাল প্রোগ্রামিং (এফপি)")।
  2. তারপরে, আমাদের প্রথম বড় সমস্যা রয়েছে। এটি একটি প্রোগ্রাম:

    f(x) = 2 * x

    g(x,y) = x / y

    প্রথমে মৃত্যুদন্ড কার্যকর করার বিষয়টি কীভাবে আমরা বলতে পারি ? কীভাবে আমরা ফাংশনগুলির চেয়ে বেশি ব্যবহার না করে ফাংশনগুলির (যেমন একটি প্রোগ্রাম ) একটি আদেশযুক্ত ক্রম গঠন করতে পারি ?

    সমাধান: রচনা ফাংশন । আপনি যদি প্রথমে চান gএবং তারপর f, শুধু লিখুন f(g(x,y))। এই ভাবে, "প্রোগ্রাম" এর একটি ফাংশন হিসাবে ভাল: main = f(g(x,y))। ঠিক আছে কিন্তু ...

  3. আরও সমস্যা: কিছু ফাংশন ব্যর্থ হতে পারে (যেমন g(2,0), 0 দ্বারা ভাগ)। আমরা আশা করি আপনি না "ব্যতিক্রম" FP মধ্যে (একটি ব্যতিক্রম কোন ফাংশন নয়)। আমরা কীভাবে এটি সমাধান করব?

    সমাধান: আসুন ফাংশনগুলিকে দুটি ধরণের জিনিস ফিরিয়ে আনতে দেওয়া যাক: g : Real,Real -> Realদুটি ক্ষেত্র থেকে দুটি বাস্তবের মধ্যে ফাংশন রাখার পরিবর্তে আসুন g : Real,Real -> Real | Nothing(দুটি বাস্তব থেকে (বাস্তব বা কিছুই নয়) ফাংশনটি ) অনুমতি দিন ।

  4. তবে ফাংশনগুলি (সহজ হতে) কেবল একটি জিনিস ফেরত দেওয়া উচিত ।

    সমাধান: আসুন ফেরত দেওয়ার জন্য একটি নতুন ধরণের ডেটা তৈরি করুন, একটি " বক্সিং টাইপ " যা সম্ভবত একটি বাস্তবকে আবদ্ধ করে দেয় বা কেবল কিছুই নয়। সুতরাং, আমরা থাকতে পারে g : Real,Real -> Maybe Real। ঠিক আছে কিন্তু ...

  5. এখন কি হবে f(g(x,y))? fএকটি গ্রাস করতে প্রস্তুত নয় Maybe Real। এবং, আমরা প্রতি ফাংশন আমরা এর সাথে যুক্ত হতে পারে পরিবর্তন করতে চাই না gএকটি গ্রাস Maybe Real

    সমাধান: আসুন "সংযুক্ত" / "রচনা" / "লিঙ্ক" ফাংশনগুলির জন্য একটি বিশেষ ক্রিয়া করি । এইভাবে, আমরা, পর্দার আড়ালে, একটি ফাংশন আউটপুট নীচের একটি খাওয়ানোর জন্য অভিযোজিত করতে পারেন।

    আমাদের ক্ষেত্রে ইন: g >>= f(কানেক্ট / রচনা gকরতে f)। আমরা আউটপুট >>=পেতে চাই g, এটি পরিদর্শন করতে চাই এবং যদি এটি Nothingকেবল কল না করে fফিরে আসে Nothing; বা বিপরীতে, বাক্সটি বের করুন Realএবং fএটি দিয়ে ফিড দিন। (এই অ্যালগরিদম কেবল প্রকারের >>=জন্য বাস্তবায়ন Maybe)। এছাড়াও নোট করুন যে প্রতি "বক্সিং টাইপ" (ভিন্ন বাক্স, বিভিন্ন অ্যাডাপ্টিং অ্যালগরিদম) প্রতি একবারই>>= লিখতে হবে ।

  6. অন্যান্য অনেক সমস্যা দেখা দেয় যা একই প্যাটার্নটি ব্যবহার করে সমাধান করা যেতে পারে: ১. বিভিন্ন অর্থ / মানকে কোডিফ / সংরক্ষণ করতে একটি "বাক্স" ব্যবহার করুন এবং gসেই "বাক্সযুক্ত মানগুলি" ফিরিয়ে দেওয়ার মতো ফাংশন রাখুন । ২. এর আউটপুটটির ইনপুটটিতে g >>= fসংযোগে সহায়তা করার জন্য কোনও সুরকার / লিঙ্কার রাখুন , সুতরাং আমাদের কোনও পরিবর্তন করতে হবে না ।gff

  7. এই কৌশলটি ব্যবহার করে সমাধানযোগ্য সমস্যাগুলি হ'ল:

    • একটি বিশ্বব্যাপী অবস্থা রয়েছে যে ক্রমানুসারে প্রতিটি ক্রিয়াকলাপ ("প্রোগ্রাম") ভাগ করতে পারে: সমাধান StateMonad

    • আমরা "অপরিষ্কার ফাংশন" পছন্দ করি না: ফাংশনগুলি যা একই ইনপুটটির জন্য আলাদা আউটপুট দেয় । সুতরাং, আসুন সেই ফাংশনগুলি চিহ্নিত করুন, যাতে তারা ট্যাগ / বাক্সযুক্ত মানটি ফিরিয়ে দেয়: মোনাড।IO

মোট সুখ!


64
@ কার্ল দয়া করে আমাদের আলোকিত করার জন্য আরও ভাল উত্তর লিখুন
এক্সআরএক্সআর

15
@ কার্ল আমি মনে করি যে উত্তরে এটি পরিষ্কার হয়ে গেছে যে এই প্যাটার্নটি (point দফা) থেকে উপকৃত অনেকগুলি সমস্যা রয়েছে এবং সেই IOমনোড তালিকার আরও একটি সমস্যা IO(point দফা)। অন্যদিকে IOকেবল একবার এবং শেষে উপস্থিত হয়, সুতরাং, আপনার "বেশিরভাগ সময় কথা বলা ... আইও সম্পর্কে" বোঝেন না।
cibercitizen1

4
মনড সম্পর্কে দুর্দান্ত ভুল ধারণা: রাষ্ট্র সম্পর্কে মনড; ব্যতিক্রম পরিচালনা সম্পর্কে monads; মনড ছাড়া খাঁটি এফপিএলে আইও প্রয়োগ করার কোনও উপায় নেই; সোনাদাগুলি দ্ব্যর্থহীন (চুক্তিবদ্ধ Either)। সর্বাধিক উত্তরটি "আমাদের ফান্ট্যাক্টরগুলির দরকার কেন?" সম্পর্কে।
ভ্লাস্টাচু

4
"2.. 2. এর আউটপুটটির ইনপুটটিতে g >>= fসংযোগ স্থাপনে সহায়তা করার জন্য একটি সুরকার / লিঙ্কার আছে , সুতরাং আমাদের কোনও পরিবর্তন করতে হবে না ।" gff এটি মোটেই ঠিক নয়। আগে, মধ্যে f(g(x,y)), fকিছু উত্পাদন করতে পারে। এটা হতে পারে f:: Real -> String। "মোনাডিক রচনা" দিয়ে এটি উত্পাদন করতে অবশ্যই পরিবর্তন করা উচিতMaybe String , অন্যথায় প্রকারগুলি মাপসই হয় না। তাছাড়া >>=নিজেও ফিট করে না !! এটা >=>যে, এই রচনা করে না >>=। কার্লের উত্তরের অধীনে ডিফিউয়ারের সাথে আলোচনাটি দেখুন।
নেস

3
আপনার উত্তরটি এই অর্থে সঠিক যে ম্যানডস আইএমও প্রকৃতপক্ষে "ফাংশনগুলি" (ক্লাইসলি তীরগুলি সত্যই) এর রচনা / সত্ত্বা সম্পর্কে সবচেয়ে ভালভাবে বর্ণনা করা হয়েছে, তবে কোন ধরণের কী রয়েছে তার সঠিক বিবরণ তাদের "মনাদ" তৈরি করে। আপনি সমস্ত ধরণের আচরণের মধ্যে বাক্সগুলি তারের করতে পারেন (যেমন ফান্টেক্টর ইত্যাদি)। তাদের একসাথে তারের এই নির্দিষ্ট উপায়টি "মোনাড" সংজ্ঞায়িত করে।
নেস

219

উত্তরটি অবশ্যই, "আমরা না" । সমস্ত বিমূর্ততা হিসাবে, এটি প্রয়োজন হয় না।

হাস্কেলের কোনও মোনাড বিমূর্তনের প্রয়োজন নেই। খাঁটি ভাষায় আইও করার জন্য এটি প্রয়োজনীয় নয়। 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। তারা বিভাগের আইন মেনে একটি যথাযথ বিভাগ একত্র করতে পারে এমন প্রতিটি ধরণের সাথে কাজ করে।

  1. বাম পরিচয়: id . f=f
  2. সঠিক পরিচয়: f . id=f
  3. সহযোগিতা: f . (g . h)=(f . g) . h

যতক্ষণ আপনি প্রমাণ করতে পারেন যে আপনার ধরণটি এই তিনটি আইন মেনে চলে, আপনি এটিকে ক্লাইসলি বিভাগে পরিণত করতে পারেন। আর এ নিয়ে বড় কথা কী? ঠিক আছে, দেখা গেছে যে মনাদগুলি ক্লাইসলি বিভাগগুলির মতো একই জিনিস। Monadএর returnক্লাইসলির মতোই idMonadএর (>>=)Kleisli অভিন্ন নয় (.), কিন্তু এটি সক্রিয় আউট অন্যান্য পদ খুব সহজ প্রতিটি লিখতে হবে। এবং বিভাগ আইন একসংখ্যা আইন, যখন আপনি তাদের মধ্যে পার্থক্য জুড়ে অনুবাদ মতই (>>=)এবং (.)

তাহলে এত ঝামেলা করে কেন? Monadভাষাতে কেন বিমূর্ততা রয়েছে? আমি উপরে বর্ণিত হিসাবে এটি কোড পুনরায় ব্যবহার সক্ষম করে। এমনকি কোডটি পুনরায় ব্যবহারকে দুটি ভিন্ন মাত্রা সহ সক্ষম করে।

কোড পুনরায় ব্যবহারের প্রথম মাত্রা সরাসরি বিমূর্ততার উপস্থিতি থেকে আসে। আপনি কোডটি লিখতে পারেন যা বিমূর্ততার সমস্ত দৃষ্টান্ত জুড়ে কাজ করে। পুরো মোনাড-লুপস প্যাকেজটিতে লুপগুলি রয়েছে যা কোনও উদাহরণ সহ কাজ করে Monad

দ্বিতীয় মাত্রা অপ্রত্যক্ষ, কিন্তু এটি রচনাটির অস্তিত্ব থেকে অনুসরণ করে। যখন রচনাটি সহজ হয়, ছোট, পুনরায় ব্যবহারযোগ্য অংশগুলিতে কোড লেখা স্বাভাবিক। এটি একই পদ্ধতিতে (.)অপারেটরকে ছোট, পুনরায় ব্যবহারযোগ্য ফাংশনগুলি লেখার জন্য উত্সাহ দেয়।

তাহলে বিমূর্ততা কেন বিদ্যমান? কারণ এটি এমন একটি সরঞ্জাম হিসাবে প্রমাণিত হয়েছে যা কোডে আরও রচনা তৈরি করতে সক্ষম করে, যার ফলে পুনরায় ব্যবহারযোগ্য কোড তৈরি করা যায় এবং আরও পুনরায় ব্যবহারযোগ্য কোড তৈরি করতে উত্সাহিত করা হয়। কোড পুনর্ব্যবহার প্রোগ্রামিংয়ের অন্যতম পবিত্র গ্রিল। মোনাড বিমূর্ততা বিদ্যমান কারণ এটি আমাদের সেই পবিত্র গ্রেিলের দিকে একটু এগিয়ে নিয়ে যায়।


2
আপনি সাধারণত বিভাগ এবং Kleisli বিভাগের মধ্যে সম্পর্ক ব্যাখ্যা করতে পারেন? আপনার বর্ণিত তিনটি আইন যে কোনও বিভাগে ধারণ করে।
dfeuer

1
@ dfeuer ওহ এটি কোড লাগাতে newtype Kleisli m a b = Kleisli (a -> m b),। ক্লাইসলি বিভাগগুলি এমন ফাংশন যেখানে শ্রেণীবদ্ধ রিটার্ন টাইপ ( bএই ক্ষেত্রে) কোনও টাইপ নির্মাণকারীর পক্ষে যুক্তি m। আইএফ Kleisli mএকটি বিভাগ গঠন করে, mএটি একটি মোনাড।
কার্ল

1
একটি শ্রেণিবদ্ধ রিটার্ন টাইপ কি? Kleisli mএকটি বিভাগ যার যে বস্তু থেকে তীর Haskell, ধরনের এবং এই ধরনের হয় গঠন বলে মনে হয় aকরার bথেকে ফাংশন হয় aকরার m bসঙ্গে, id = returnএবং (.) = (<=<)। এটি কি সঠিক, বা আমি বিভিন্ন স্তরের জিনিস বা কোনও কিছু মিশ্রিত করছি?
ডিএফইউয়ার 26'15

1
@dfeuer এটি সঠিক বস্তুগুলি সমস্ত প্রকারের, এবং রূপগুলি প্রকারভেদগুলির মধ্যে aএবং bতবে সেগুলি সাধারণ ফাংশন নয়। mফাংশনের রিটার্ন ভ্যালুতে তারা অতিরিক্ত দিয়ে সজ্জিত ।
কার্ল

1
বিভাগীয় তত্ত্বের পরিভাষাটি কি সত্যই প্রয়োজন? সম্ভবত, আপনি যদি ধরণের ছবিগুলি ছবিগুলিতে পরিণত করেন তবে ছবিগুলি কীভাবে ছবি আঁকা হয় তার জন্য প্রকারটি ডিএনএ হয়ে থাকে (যদিও * নির্ভরশীল প্রকারগুলি) এবং তারপরে আপনি নামটি ছোট রুবি বর্ণের সাথে আপনার প্রোগ্রামটি লেখার জন্য ছবিটি ব্যবহার করেন আইকন উপরে।
aoeu256

24

বেনজামিন পিয়ার্স টিএপিএলে ড

কোনও প্রোগ্রামে শর্তাদির রান-টাইম আচরণগুলির জন্য এক ধরণের স্থিতিশীল আনুমানিক গণনা হিসাবে একটি ধরণের সিস্টেমকে বিবেচনা করা যেতে পারে।

একারণে শক্তিশালী টাইপ সিস্টেমে সজ্জিত কোনও ভাষা স্বল্প টাইপিত ভাষার চেয়ে কঠোরভাবে উদ্বেগজনক। আপনি একইভাবে মনাদদের সম্পর্কে ভাবতে পারেন।

@ কার্ল এবং সিগফাই পয়েন্ট হিসাবে , আপনি মোনাড, টাইপক্লাস বা অন্য যে কোনও বিমূর্ত স্টাফের অবলম্বন না করে আপনি চান সমস্ত অপারেশন দিয়ে একটি ডেটাটাইপ সজ্জিত করতে পারেন। তবে ম্যানডস আপনাকে কেবল পুনরায় ব্যবহারযোগ্য কোডটি লেখার অনুমতি দেয় না, সমস্ত রিন্ডান্ট ডিটাইলগুলি বিমূর্ত করতে দেয়।

উদাহরণ হিসাবে, ধরুন আমরা একটি তালিকা ফিল্টার করতে চাই। সহজ উপায় হ'ল 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একসংখ্যা, যে ফণা অধীনে ব্যবহার করা হয়, শুধু একজন সাধারণ ডাটাটাইপ হচ্ছে।

এই উদাহরণটি উদাহরণস্বরূপ বোঝায় যে, মোনাডগুলি আপনাকে কেবল গণ্যসংক্রান্ত প্রসঙ্গটি বিমূর্ত করতে এবং পরিষ্কার পুনরায় ব্যবহারযোগ্য কোড লিখতে দেয় না (মনডের সংশ্লেষের কারণে, @ কার্ল ব্যাখ্যা করেছেন), তবে ব্যবহারকারী-সংজ্ঞায়িত ডেটাটাইপগুলি এবং অন্তর্নির্মিত আদিমকে অভিন্ন হিসাবে চিকিত্সা করার জন্য।


1
এই উত্তরটি ব্যাখ্যা করে, কেন আমাদের মোনাড টাইপক্লাসের প্রয়োজন। বোঝার সর্বোত্তম উপায়, কেন আমাদের মনাদ প্রয়োজন এবং অন্য কিছু নয়, তা হল মনাদ এবং প্রয়োগমূলক ফান্ট্যাক্টারের মধ্যে পার্থক্য সম্পর্কে পড়া । এক , দুটি
user3237465

20

আমি মনে করি না যে 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

এতোটা খারাপ না!

সুতরাং এই সব কি আছে monades সঙ্গে কি?

অনুশীলনে, আপনি আপনার সমস্ত প্রোগ্রাম সংজ্ঞায়িত করার জন্য সরল নির্মাণকারী ব্যবহার করতে চাইবেন না। এ জাতীয় মৌলিক নির্মাণকারীদের একটি ভাল দম্পতি থাকা দরকার, তবুও বেশিরভাগ উচ্চ-স্তরের স্টাফের জন্য আমরা কিছু সুন্দর উচ্চ-স্তরের স্বাক্ষর সহ একটি ফাংশন লিখতে চাই। এটি দেখা যায় যে এর বেশিরভাগটি একই রকম দেখাবে: কিছু ধরণের অর্থপূর্ণ-টাইপযুক্ত মান গ্রহণ করুন, এবং ফলাফল হিসাবে একটি আইও ক্রিয়া অর্জন করুন।

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

স্পষ্টতই এটি আইও এর কার্যকর প্রয়োগ নয়, তবে এটি নীতিগতভাবে ব্যবহারযোগ্য।


@jdlugosz: IO3 a ≡ Cont IO2 a। তবে আমি বোঝাতে চেয়েছিলাম যে যারা ইতিমধ্যে ধারাবাহিকতা মোনাদকে জানেন তাদের পক্ষে এই মন্তব্যটি আরও একটি সম্মতি হিসাবে জানানো হয়েছে, কারণ এটির সাথে ঠিক বান্ধব হিসাবে খ্যাতি নেই।
বাম দিকের বাইরে

4

এক শ্রেণীর পুনরাবৃত্ত সমস্যাগুলি সমাধান করার জন্য মনডস কেবল একটি সুবিধাজনক কাঠামো। প্রথমত, 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"]

এটি লক্ষ করা উচিত যে তালিকা মোনাড কোনওভাবেই কোনও পার্শ্ব-প্রভাবের গণনা নেই। একটি গাণিতিক কাঠামো একটি মনাদ (অর্থাত্ উল্লিখিত ইন্টারফেস এবং আইন মেনে চলা) পার্শ্ব প্রতিক্রিয়া বোঝায় না, যদিও পার্শ্ব-প্রতিক্রিয়াশীল ঘটনাটি বেশিরভাগভাবে মোনাদিক কাঠামোর সাথে মাপসই হয়।


3

মনডগুলি মূলত একটি শৃঙ্খলে একসাথে ফাংশন রচনা করতে পরিবেশন করে। সময়কাল।

এখন তারা রচনা করার পদ্ধতিটি বিদ্যমান মনাদগুলিতে পার্থক্য করে, ফলে বিভিন্ন আচরণের ফলে (যেমন, রাজ্য মনাদে পরিবর্তনীয় অবস্থার অনুকরণ করা)।

স্নাতকদের সম্পর্কে বিভ্রান্তিটি হ'ল এত সাধারণ, অর্থাত্ কার্যকারিতা রচনা করার একটি পদ্ধতি, এগুলি অনেক কিছুর জন্য ব্যবহার করা যেতে পারে, ফলে লোকেরা বিশ্বাস করতে পারে যে মনডগুলি রাষ্ট্র সম্পর্কে, আইও সম্পর্কিত, ইত্যাদি, যখন তারা কেবল "রচনা কার্য" সম্পর্কে থাকে "।

এখন, মনাদাদের সম্পর্কে একটি আকর্ষণীয় বিষয় হ'ল রচনাটির ফলাফলটি সর্বদা "এম এ" টাইপ হয়, এটি একটি এমফের সাথে ট্যাগ করা একটি খামের অভ্যন্তরের একটি মান। এই বৈশিষ্ট্যটি বাস্তবায়িত করতে সত্যিই দুর্দান্ত বলে মনে হয়, উদাহরণস্বরূপ, অশুচি কোড থেকে খাঁটি মধ্যে স্পষ্ট বিভাজন: সমস্ত অপরিষ্কার ক্রিয়াকলাপ "IO a" টাইপ হিসাবে ঘোষণা করুন এবং আইও মোনাডকে সংজ্ঞায়িত করার সময় কোনও কার্য সরবরাহ করবেন না, " "আইও এ" এর ভিতরে থেকে একটি মান। ফলাফলটি হ'ল কোনও ফাংশন বিশুদ্ধ হতে পারে না এবং একই সাথে একটি "আইও এ" থেকে একটি মানও বের করে নিতে পারে, কারণ খাঁটি থাকার সময় এই জাতীয় মান নেওয়ার কোনও উপায় নেই (ফাংশনটি অবশ্যই "আইও" মনডের অভ্যন্তরে থাকতে হবে) যেমন মান)। (দ্রষ্টব্য: ভাল, কিছুই নিখুঁত নয়, তাই "IO straitjacket" কে "অনিরাপদ পারফর্মফর্মআইও: আইও এ -> এ" ব্যবহার করে ভাঙা যায়


2

আপনার যদি কোনও ধরণের কনস্ট্রাক্টর এবং ফাংশন থাকে যা সেই ধরণের পরিবারের মানগুলি ফিরিয়ে দেয় তবে আপনার স্নাতকের প্রয়োজন । অবশেষে, আপনি চাই একসঙ্গে ফাংশন এই ধরনের একত্রিতকেন উত্তর দেওয়ার জন্য এই তিনটি মূল উপাদান ।

আমাকে বিস্তারিত জানাতে দিন। আপনার আছে Int, Stringএবং Realটাইপ এর ফাংশন Int -> String, String -> Realইত্যাদি। আপনি এই ফাংশনগুলি সহজেই একত্রিত করতে পারেন, শেষ করে Int -> Real। জীবন সুন্দর.

তারপরে, একদিন আপনার নতুন ধরণের পরিবার তৈরি করতে হবে । এটি হতে পারে কারণ আপনার কোনও মূল্য ( Maybe) প্রদান না করে, একটি ত্রুটি ( Either), একাধিক ফলাফল ( List) ফিরিয়ে দেওয়ার সম্ভাবনা বিবেচনা করতে হবে ।

লক্ষ্য করুন যে Maybeএটি একটি টাইপ কনস্ট্রাক্টর। এটি একটি ধরণের লাগে, যেমন Intএবং নতুন টাইপ দেয় Maybe Int। প্রথম জিনিসটি মনে রাখবেন, কোনও টাইপ কনস্ট্রাক্টর নেই, কোনও মোনাদ নেই।

অবশ্যই, আপনি আপনার কোডে আপনার টাইপ কনস্ট্রাক্টর ব্যবহার করতে চান এবং শীঘ্রই আপনি Int -> Maybe Stringএবং এর মতো ফাংশনগুলি দিয়ে শেষ করেন String -> Maybe Float। এখন, আপনি সহজেই আপনার কার্যগুলি একত্রিত করতে পারবেন না। জীবন আর ভাল হয় না।

মনডা উদ্ধারে আসার সময় এখানে। তারা আপনাকে আবার এই জাতীয় ফাংশন একত্রিত করার অনুমতি দেয়। আপনার কেবল রচনাটি পরিবর্তন করা দরকার জন্য > ==


2
টাইপ পরিবারগুলির সাথে এটির কোনও সম্পর্ক নেই। আপনি আসলে কি সম্পর্কে কথা বলছেন?
dfeuer
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.