"ফ্রি মোনাড + ইন্টারপ্রিটার" প্যাটার্নটি কী?


95

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

আমি (যেমন পোস্ট থেকে বুঝতে এই এটি ডেটা-অ্যাক্সেস থেকে মডেল পৃথক সম্পর্কে)। এটি কীভাবে সুপরিচিত সংগ্রহস্থল প্যাটার্ন থেকে আলাদা? তাদের একই অনুপ্রেরণা রয়েছে বলে মনে হয়।

উত্তর:


138

প্রকৃত প্যাটার্নটি কেবলমাত্র ডেটা অ্যাক্সেসের চেয়ে উল্লেখযোগ্যভাবে সাধারণ। এটি একটি ডোমেন-নির্দিষ্ট ভাষা তৈরির একটি হালকা উপায় যা আপনাকে একটি এএসটি দেয় এবং তারপরে আপনার পছন্দ মতো এএসটি "চালানো" করার জন্য এক বা একাধিক দোভাষী করে।

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

একটি নিখরচায় মোনাদ ব্যবহার আপনাকে একটি কম্পোজেবল ডিএসএল এর কাঠামো দেয় ; আপনাকে যা করতে হবে তা হ'ল টুকরোগুলি নির্দিষ্ট করে specify আপনি কেবল একটি ডেটা টাইপ লিখুন যা আপনার ডিএসএল-এর সমস্ত ক্রিয়াকে অন্তর্ভুক্ত করে। এই ক্রিয়াগুলি কেবল ডেটা অ্যাক্সেস নয়, কিছু করতে পারে। তবে, আপনি যদি আপনার সমস্ত ডেটা অ্যাক্সেসগুলি ক্রিয়া হিসাবে নির্দিষ্ট করে থাকেন তবে আপনি একটি এএসটি পাবেন যা ডেটা স্টোরটিতে সমস্ত প্রশ্ন এবং আদেশগুলি নির্দিষ্ট করে। এরপরে আপনি নিজের পছন্দ মতো এটি ব্যাখ্যা করতে পারেন: লাইভ ডাটাবেসের বিরুদ্ধে এটি চালান, একটি উপহাসের বিরুদ্ধে চালান, কেবল ডিবাগিংয়ের জন্য কমান্ডগুলি লগ করুন বা কোয়েরিগুলি অনুকূলিত করার চেষ্টা করুন।

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

data DSL next = Get String (String -> next)
              | Set String String next
              | End

nextপরামিতি আমাদের কর্ম একত্রিত করতে দেয়। আমরা এটি এমন একটি প্রোগ্রাম লিখতে ব্যবহার করতে পারি যা "foo" পায় এবং সেই মানটির সাথে "বার" সেট করে:

p1 = Get "foo" $ \ foo -> Set "bar" foo End

দুর্ভাগ্যক্রমে, অর্থবহ ডিএসএল-এর পক্ষে এটি যথেষ্ট নয়। যেহেতু আমরা nextকম্পোজিশনের জন্য ব্যবহার করেছি , প্রকারটি p1আমাদের প্রোগ্রামের সমান দৈর্ঘ্য (যেমন 3 টি আদেশ):

p1 :: DSL (DSL (DSL next))

এই বিশেষ উদাহরণে, এর nextমতো ব্যবহার করা কিছুটা অদ্ভুত বলে মনে হয়, তবে আমরা যদি আমাদের ক্রিয়াগুলির বিভিন্ন ধরণের ভেরিয়েবল রাখতে চাই তবে এটি গুরুত্বপূর্ণ। আমরা উদাহরণস্বরূপ কোনও টাইপ করতে চাই getএবং চাই set

nextপ্রতিটি ক্রিয়াকলাপের জন্য ক্ষেত্রটি কীভাবে আলাদা তা লক্ষ্য করুন । এটি ইঙ্গিত দেয় যে আমরা এটি DSLএকটি ফান্টর তৈরি করতে ব্যবহার করতে পারি :

instance Functor DSL where
  fmap f (Get name k)          = Get name (f . k)
  fmap f (Set name value next) = Set name value (f next)
  fmap f End                   = End

প্রকৃতপক্ষে, এটিকে একটি ফান্টাকর করার একমাত্র বৈধ উপায়, সুতরাং আমরা এক্সটেনশানটি derivingসক্ষম করে স্বয়ংক্রিয়ভাবে দৃষ্টান্তটি তৈরি করতে পারি DeriveFunctor

পরবর্তী পদক্ষেপটি Freeনিজেই টাইপ। এটাই আমরা আমাদের এএসটি কাঠামোর প্রতিনিধিত্ব করতে , DSLটাইপের শীর্ষে তৈরি করতে ব্যবহার করি । আপনি এটিকে ধরণের স্তরের তালিকার মতো ভাবতে পারেন , যেখানে "কনস" কেবল কোনও ফান্টারের মতো বাসা বাঁধছে DSL:

-- compare the two types:
data Free f a = Free (f (Free f a)) | Return a
data List a   = Cons a (List a)     | Nil

সুতরাং আমরা Free DSL nextবিভিন্ন ধরণের প্রোগ্রাম একই ধরণের দিতে ব্যবহার করতে পারি :

p2 = Free (Get "foo" $ \ foo -> Free (Set "bar" foo (Free End)))

যার অনেক ভাল টাইপ রয়েছে:

p2 :: Free DSL a

তবে এর সমস্ত নির্মাণকারীর সাথে প্রকৃত অভিব্যক্তিটি এখনও ব্যবহারের জন্য খুব বিশ্রী! এইখানেই মোনাড অংশটি আসে। "ফ্রি মোনাড" নামটি Freeযেমন বোঝা যাচ্ছে, ততক্ষণ এটি একটি মোনাড as যতক্ষণ fনা (এই ক্ষেত্রে DSL) ফান্টিকার হিসাবে থাকে:

instance Functor f => Monad (Free f) where
  return         = Return
  Free a >>= f   = Free (fmap (>>= f) a)
  Return a >>= f = f a

এখন আমরা কোথাও পাচ্ছি: আমরা doআমাদের ডিএসএল এক্সপ্রেশনকে আরও সুন্দর করতে স্বরলিপিটি ব্যবহার করতে পারি । একমাত্র প্রশ্ন কি জন্য রাখা next? ঠিক আছে, ধারণাটি Freeরচনাটির জন্য কাঠামোটি ব্যবহার করা উচিত , সুতরাং আমরা কেবলমাত্র Returnপ্রতিটি পরবর্তী ক্ষেত্রের জন্য রাখব এবং করণীয়করণটি সমস্ত নদীর গভীরতানির্ণয়টি করুক:

p3 = do foo <- Free (Get "foo" Return)
        Free (Set "bar" foo (Return ()))
        Free End

এটি আরও ভাল তবে এটি এখনও কিছুটা বিশ্রী। আমরা Freeএবং Returnসমস্ত জায়গা আছে। সুখের বিষয়, আমরা ব্যবহার করতে পারি এমন একটি প্যাটার্ন রয়েছে: একটি ডিএসএল ক্রিয়াটি আমরা যেভাবে "উত্তোলন" করি Freeতা সর্বদা একই — আমরা এটিকে আবদ্ধ করি Freeএবং এর Returnজন্য আবেদন করি next:

liftFree :: Functor f => f a -> Free f a
liftFree action = Free (fmap Return action)

এখন এটি ব্যবহার করে আমরা আমাদের প্রতিটি কমান্ডের দুর্দান্ত সংস্করণ লিখতে পারি এবং একটি সম্পূর্ণ ডিএসএল পেতে পারি:

get key       = liftFree (Get key id)
set key value = liftFree (Set key value ())
end           = liftFree End

এটি ব্যবহার করে, আমরা কীভাবে আমাদের প্রোগ্রামটি লিখতে পারি তা এখানে:

p4 :: Free DSL a
p4 = do foo <- get "foo"
        set "bar" foo
        end

ঝরঝরে কৌতুকটি হ'ল p4সামান্য অত্যাবশ্যক প্রোগ্রামের মতো দেখতে এটি আসলে একটি অভিব্যক্তি যার মূল্য রয়েছে

Free (Get "foo" $ \ foo -> Free (Set "bar" foo (Free End)))

সুতরাং, নিদর্শন মুক্ত মোনাদ অংশটি আমাদের একটি ডিএসএল পেয়েছে যা সুন্দর সিনট্যাক্স সহ বাক্য গঠন করে। আমরা ব্যবহার না করে কম্পোজেবল উপ-গাছগুলিও লিখতে পারি End; উদাহরণস্বরূপ, আমরা থাকতে পারি followযা একটি চাবি নেয়, তার মান পায় এবং তারপরে এটি কী হিসাবে নিজেই ব্যবহার করে:

follow :: String -> Free DSL String
follow key = do key' <- get key
                get key'

এখন followআমাদের প্রোগ্রামগুলিতে ঠিক যেমন getবা ব্যবহার করা যেতে পারে set:

p5 = do foo <- follow "foo"
        set "bar" foo
        end

সুতরাং আমরা আমাদের ডিএসএল এর জন্যও কিছু সুন্দর রচনা এবং বিমূর্ততা পেয়েছি।

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

runIO :: Free DSL a -> IO ()
runIO (Free (Get key k)) =
  do res <- getKey key
     runIO $ k res
runIO (Free (Set key value next)) =
  do setKey key value
     runIO next
runIO (Free End) = close
runIO (Return _) = return ()

এটি আনন্দের DSLসাথে যে কোনও খণ্ডকে, এমনকি শেষ না করেও মূল্যায়ন করবে end। আনন্দের সাথে, আমরা ফাংশনটির একটি "নিরাপদ" সংস্করণ তৈরি করতে পারি endযা ইনপুট টাইপের স্বাক্ষর সেট করে কেবলমাত্র বন্ধ হওয়া প্রোগ্রামগুলি গ্রহণ করে (forall a. Free DSL a) -> IO ()। পুরাতন স্বাক্ষর একটি গ্রহণ করার সময় Free DSL aজন্য কোন a (যেমন Free DSL String, Free DSL Intইত্যাদি), এই সংস্করণ শুধুমাত্র একটি গ্রহণ Free DSL aযে জন্য কাজ করে যে সম্ভব a-যেটা আমরা কেবল সঙ্গে তৈরি করতে পারেন end। এটি গ্যারান্টি দেয় যে আমাদের কাজ শেষ হয়ে গেলে আমরা সংযোগটি বন্ধ করতে ভুলব না।

safeRunIO :: (forall a. Free DSL a) -> IO ()
safeRunIO = runIO

(আমরা কেবল runIOএই ধরণেরটি দিয়ে শুরু করতে পারি না কারণ এটি আমাদের পুনরাবৃত্ত কলের জন্য সঠিকভাবে কাজ করবে না However তবে, আমরা সংজ্ঞাটিকে runIOএকটি whereব্লকের মধ্যে স্থানান্তর করতে safeRunIOএবং ফাংশনের উভয় সংস্করণ প্রকাশ না করে একই প্রভাব পেতে পারি))

আমাদের কোড চালানো IOকেবলমাত্র আমরা যা করতে পারি তা নয়। পরীক্ষার জন্য, আমরা এটির State Mapপরিবর্তে খাঁটির বিরুদ্ধে চালাতে চাই । কোডটি লেখা ভাল অনুশীলন।

সুতরাং এটি ফ্রি মোনাড + ইন্টারপ্রেটার প্যাটার্ন। সমস্ত নদীর গভীরতানির্ণয় করার জন্য ফ্রি মনাদ কাঠামোর সুযোগ নিয়ে আমরা একটি ডিএসএল তৈরি করি। আমরা আমাদের ডিএসএলের সাথে ড-নোটেশন এবং স্ট্যান্ডার্ড মোনাড ফাংশন ব্যবহার করতে পারি। তারপরে, আসলে এটি ব্যবহার করতে, আমাদের এটি কোনওভাবে ব্যাখ্যা করতে হবে; যেহেতু গাছটি শেষ পর্যন্ত কেবলমাত্র একটি ডেটা কাঠামো, তাই আমরা এটির ব্যাখ্যা করতে পারি তবে আমরা বিভিন্ন উদ্দেশ্যে পছন্দ করি।

যখন আমরা এটি কোনও বাহ্যিক ডেটা স্টোরের অ্যাক্সেসগুলি পরিচালনা করতে ব্যবহার করি, এটি প্রকৃতপক্ষে সংগ্রহস্থল প্যাটার্নের অনুরূপ। এটি আমাদের ডেটা স্টোর এবং আমাদের কোডের মধ্যে মধ্যস্থতা করে, দুটিকে আলাদা করে। যদিও কিছু উপায়ে এটি আরও সুনির্দিষ্ট: "সংগ্রহশালা" হ'ল একটি স্পষ্টত এএসটি সহ একটি ডিএসএল যা আমরা তারপরে আমাদের পছন্দ মতো ব্যবহার করতে পারি।

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


6
একে 'ফ্রি' মোনাড বলা হয় কেন?
বেনিয়ামিন হজসন

14
"ফ্রি" নামটি বিভাগের তত্ত্ব থেকে এসেছে: ncatlab.org/nlab/show/free+object তবে এর সাজানোর অর্থ এটি "ন্যূনতম" মোনাদ - এটির মধ্যে কেবল বৈধ ক্রিয়াকলাপগুলি মোনাড অপারেশন, যেমন এটি আছে " ভুলে গেছেন "এটি সমস্ত কাঠামো।
বয়ড স্টিফেন স্মিথ জুনিয়র

3
@ বেঞ্জামিনহডসন: বয়ড পুরোপুরি ঠিক আছে। আপনি কেবল কৌতূহলী না হলে আমি এটি নিয়ে খুব বেশি চিন্তা করব না। ড্যান পিপোনি বেহ্যাকে "ফ্রি" এর অর্থ কী তা নিয়ে দুর্দান্ত আলোচনা করেছিলেন, যা দেখার মতো worth তার স্লাইডগুলি অনুসরণ করার চেষ্টা করুন কারণ ভিডিওতে দৃশ্যটি সম্পূর্ণ অকেজো use
টিখন জেলভিস

3
একটি নিটপিক: "মুক্ত মোনাড অংশটি কেবলমাত্র [আমার জোর] একটি এএসটি পাওয়ার সহজ উপায় যা আপনি প্রচুর কাস্টম কোড না লিখেই হাস্কেলের স্ট্যান্ডার্ড মোনাড সুবিধাগুলি (ডু-নোটেশন) ব্যবহার করে জড়ো করতে পারেন" " এটি "ন্যায়বিচার" এর চেয়ে বেশি (যেমনটি আমি নিশ্চিত আপনি জানেন)। ফ্রি ম্যানডসও একটি সাধারণ প্রোগ্রামের উপস্থাপনা যা দোভাষীর পক্ষে doএমন প্রোগ্রামগুলির মধ্যে পার্থক্য করা অসম্ভব করে তোলে যার নোটেশন আলাদা তবে বাস্তবে "একই অর্থ"।
সাকান্দিম

5
@ সাকুন্দিম: আপনি কি আপনার মন্তব্যে বিস্তারিত বলতে পারবেন? বিশেষত 'ফ্রি ম্যানডস' বাক্যটিও একটি সাধারণ প্রোগ্রামের উপস্থাপনা যা দোভাষীর পক্ষে এমন প্রোগ্রামগুলির মধ্যে পার্থক্য করা অসম্ভব করে তোলে যার করণীয় স্বীকৃতি আলাদা তবে বাস্তবে "একই অর্থ"।
জর্জিও

15

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

* এটি লক্ষণীয় যে এই প্যাটার্নটি মনাদদের সাথে একচেটিয়া নয়, এবং প্রকৃতপক্ষে বিনামূল্যে আবেদনকারী বা বিনামূল্যে তীরগুলির সাথে আরও কার্যকর কোড তৈরি করতে পারে । ( Parsers এই আরেকটি উদাহরণ। )


দুঃখিত, আমার সংগ্রহস্থল সম্পর্কে আরও পরিষ্কার হওয়া উচিত ছিল। (আমি ভুলে গেছি যে প্রত্যেকেরই ব্যবসায়ের সিস্টেম / ওও / ডিডিডি ব্যাকগ্রাউন্ড নেই!) একটি সংগ্রহস্থলটি মূলত ডেটা অ্যাক্সেসকে আবদ্ধ করে এবং আপনার জন্য ডোমেন অবজেক্টগুলিকে পুনরায় হাইড্রেট করে। এটি প্রায়শই নির্ভরতা বিপরীতার পাশাপাশি ব্যবহৃত হয় - আপনি রেপোর বিভিন্ন বাস্তবায়ন 'প্লাগ ইন' করতে পারেন (পরীক্ষার জন্য দরকারী, বা যদি আপনার ডাটাবেস বা ওআরএম পরিবর্তন করতে চান)। ডোমেন কোডটি কোথা থেকে ডোমেন অবজেক্টটি পেয়েছে সে repository.Get()সম্পর্কে কোনও জ্ঞান ছাড়াই কল করে ।
বেনিয়ামিন হডসন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.