পাঠক মোনাডের উদ্দেশ্য কী?


122

পাঠক মনাদ এত জটিল এবং অকেজো বলে মনে হচ্ছে। জাভা বা সি ++ এর মতো অপরিহার্য ভাষায়, আমার যদি ভুল না হয় তবে পাঠক মোনাডের জন্য কোনও সমমানের ধারণা নেই।

আপনি কি আমাকে একটি সাধারণ উদাহরণ দিতে পারেন এবং এটি কিছুটা সাফ করতে পারেন?


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

5
@ ড্যানিয়েল: এটি উত্তরের
একক নেগেশনএলিমিনেশন

@ টোকেনম্যাকগুয়ে একটি উত্তরের জন্য খুব সংক্ষিপ্ত এবং আমার আরও কিছুক্ষণ ভাবতে এখন অনেক দেরি হয়ে গেছে। অন্য কেউ না করলে আমি ঘুমানোর পরে যাব।
ড্যানিয়েল ফিশার

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

উত্তর:


169

ভয় পাবেন না! পাঠক মোনাড আসলে এত জটিল নয় এবং আসল ব্যবহারযোগ্য ইউটিলিটি রয়েছে।

একটি মনাদ কাছে যাওয়ার দুটি উপায় আছে: আমরা জিজ্ঞাসা করতে পারি

  1. একসংখ্যা কী না ? এটি কোন অপারেশন দিয়ে সজ্জিত? এটা কি জন্য ভাল?
  2. মোনাড কীভাবে প্রয়োগ করা হয়? কোথা থেকে এটি উত্থিত হয়?

প্রথম পদ্ধতির থেকে, পাঠক মনাদ কিছু বিমূর্ত প্রকারের

data Reader env a

যেমন যে

-- Reader is a monad
instance Monad (Reader env)

-- and we have a function to get its environment
ask :: Reader env env

-- finally, we can run a Reader
runReader :: Reader env a -> env -> a

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

যে কোনও সময় আপনার গণনাতে একটি "ধ্রুবক" থাকে যা আপনার বিভিন্ন পয়েন্টে প্রয়োজন, তবে সত্যই আপনি বিভিন্ন মান সহ একই গণনা সম্পাদন করতে সক্ষম হতে চান তবে আপনার পাঠক মোনাড ব্যবহার করা উচিত।

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

 import Control.Monad.Reader

 data GameState = NotOver | FirstPlayerWin | SecondPlayerWin | Tie

 data Game position
   = Game {
           getNext :: position -> [position],
           getState :: position -> GameState
          }

 getNext' :: position -> Reader (Game position) [position]
 getNext' position
   = do game <- ask
        return $ getNext game position

 getState' :: position -> Reader (Game position) GameState
 getState' position
   = do game <- ask
        return $ getState game position


 negamax :: Double -> position -> Reader (Game position) Double
 negamax color position
     = do state <- getState' position 
          case state of
             FirstPlayerWin -> return color
             SecondPlayerWin -> return $ negate color
             Tie -> return 0
             NotOver -> do possible <- getNext' position
                           values <- mapM ((liftM negate) . negamax (negate color)) possible
                           return $ maximum values

এটি তখন যে কোনও সীমাবদ্ধ, নির্জনবাদী, দুটি প্লেয়ার গেম নিয়ে কাজ করবে।

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

type CurrencyDict = Map CurrencyName Dollars
currencyDict :: CurrencyDict

স্পট দাম পেতে। তারপরে আপনি এই কোডটি আপনার কোডটিতে কল করতে পারেন .... তবে অপেক্ষা করুন! কাজ করবে না! মুদ্রার অভিধানটি অপরিবর্তনীয় এবং তাই কেবল আপনার প্রোগ্রামের জীবনের জন্যই হবে না, তবে এটি সংকলিত হওয়ার সময় থেকেই ! তো তুমি কি কর? ঠিক আছে, একটি বিকল্প হ'ল পাঠক মনাদ ব্যবহার করা:

 computePrice :: Reader CurrencyDict Dollars
 computePrice
    = do currencyDict <- ask
      --insert computation here

সম্ভবত সবচেয়ে ক্লাসিক ব্যবহারের কেস হ'ল দোভাষীকে প্রয়োগ করা। তবে, আমরা এটি দেখার আগে আমাদের আরও একটি কার্যকারিতা প্রবর্তন করতে হবে

 local :: (env -> env) -> Reader env a -> Reader env a

ঠিক আছে, সুতরাং হাস্কেল এবং অন্যান্য কার্যকরী ভাষা ল্যাম্বডা ক্যালকুলাসের উপর ভিত্তি করে । ল্যাম্বদা ক্যালকুলাসের মতো একটি সিনট্যাক্স রয়েছে

 data Term = Apply Term Term | Lambda String Term | Var Term deriving (Show)

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

 newtype Env = Env ([(String, Closure)])
 type Closure = (Term, Env)

আমাদের হয়ে গেলে, আমাদের একটি মান (বা একটি ত্রুটি) বের করা উচিত:

 data Value = Lam String Closure | Failure String

সুতরাং, আসুন দোভাষী লিখুন:

interp' :: Term -> Reader Env Value
--when we have a lambda term, we can just return it
interp' (Lambda nv t)
   = do env <- ask
        return $ Lam nv (t, env)
--when we run into a value, we look it up in the environment
interp' (Var v)
   = do (Env env) <- ask
        case lookup (show v) env of
          -- if it is not in the environment we have a problem
          Nothing -> return . Failure $ "unbound variable: " ++ (show v)
          -- if it is in the environment, then we should interpret it
          Just (term, env) -> local (const env) $ interp' term
--the complicated case is an application
interp' (Apply t1 t2)
   = do v1 <- interp' t1
        case v1 of
           Failure s -> return (Failure s)
           Lam nv clos -> local (\(Env ls) -> Env ((nv, clos) : ls)) $ interp' t2
--I guess not that complicated!

অবশেষে, আমরা একটি তুচ্ছ পরিবেশে এটি ব্যবহার করতে পারি:

interp :: Term -> Value
interp term = runReader (interp' term) (Env [])

এবং এটি হয়। ল্যাম্বডা ক্যালকুলাসের জন্য সম্পূর্ণ কার্যকারী দোভাষী inter


এ সম্পর্কে চিন্তা করার অন্য উপায়টি জিজ্ঞাসা করা: এটি কীভাবে বাস্তবায়ন করা হয়? উত্তরটি হ'ল পাঠক মোনাড প্রকৃতপক্ষে সমস্ত মনাদাদের মধ্যে অন্যতম সরল এবং মার্জিত।

newtype Reader env a = Reader {runReader :: env -> a}

পাঠক কার্যকারিতা জন্য একটি অভিনব নাম! আমরা ইতিমধ্যে সংজ্ঞায়িত করেছি runReaderতাই এপিআই এর অন্যান্য অংশগুলির কী হবে? ঠিক আছে, প্রত্যেকটি Monadএকটি Functor:

instance Functor (Reader env) where
   fmap f (Reader g) = Reader $ f . g

এখন, একটি monad পেতে:

instance Monad (Reader env) where
   return x = Reader (\_ -> x)
   (Reader f) >>= g = Reader $ \x -> runReader (g (f x)) x

যা এতটা ভীতিজনক নয়। askসত্যিই সহজ:

ask = Reader $ \x -> x

যদিও localখুব খারাপ না:

local f (Reader g) = Reader $ \x -> runReader g (f x)

ঠিক আছে, তাই পাঠক মনাদ একটি ফাংশন। পাঠক কেন আদৌ আছে? ভাল প্রশ্ন. আসলে, আপনার দরকার নেই!

instance Functor ((->) env) where
  fmap = (.)

instance Monad ((->) env) where
  return = const
  f >>= g = \x -> g (f x) x

এগুলি আরও সহজ। আরও কি, askঠিক আছে idএবং localসক্রিয় ফাংশন ক্রম সঙ্গে ফাংশন রচনা!


6
খুব আকর্ষণীয় উত্তর। সত্য, আমি আবার অনেকবার পড়ি, যখন আমি মনাদ পর্যালোচনা করতে চাই। যাইহোক, নাগাম্যাক্স অ্যালগরিদম সম্পর্কে, "মান <- মানচিত্র <(নেগেট। নেগাম্যাক্স (নেগেট রঙ)) সম্ভব" সঠিক মনে হচ্ছে না। আমি জানি যে, আপনার দেওয়া কোডটি কেবল পাঠক মনাদ কীভাবে কাজ করে তা দেখানোর জন্য। তবে আপনার যদি সময় থাকে তবে আপনি নেগাম্যাক্স অ্যালগরিদমের কোডটি সংশোধন করতে পারবেন? কারণ, এটি আকর্ষণীয়, যখন আপনি নেগাম্যাক্স সমাধানের জন্য পাঠক মোনাড ব্যবহার করেন।
chipbk10

4
সুতরাং Readermonad টাইপ শ্রেণীর কিছু নির্দিষ্ট বাস্তবায়ন সঙ্গে একটি ফাংশন হয়? এটি আগে বললে আমার কিছুটা হতবাক হতে সহায়তা হত। প্রথমে আমি তা পাচ্ছিলাম না। অর্ধেক পথ ধরে আমি ভেবেছিলাম "ওহ, এটি আপনাকে এমন কিছু ফেরত দেওয়ার অনুমতি দেয় যা আপনি একবার হারিয়ে যাওয়া মান সরবরাহ করার পরে আপনাকে পছন্দসই ফলাফল দেয় will" আমি এটি দরকারী বলে মনে করি, তবে হঠাৎ বুঝতে পেরেছিলাম যে কোনও ফাংশন ঠিক এটি করে।
ziggystar

1
এটি পড়ার পরে আমি এর বেশিরভাগটি বুঝতে পারি। যদিও localফাংশনটির আরও কিছু ব্যাখ্যা দরকার ...
ক্রিস্টোফ ডি ট্রায়ার

@ ফিলিপ আমার কাছে মোনাদ উদাহরণটি সম্পর্কে একটি প্রশ্ন আছে। আমরা যেমন বাঁধাই ফাংশন লিখতে পারি না (Reader f) >>= g = (g (f x))?
জেরোনোন

@ জেরোনোন কোথায় x?
আশিস নেগি

56

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

উদাহরণস্বরূপ, এক পর্যায়ে আমি code তিহাসিক মূল্যবোধগুলি মোকাবেলা করার জন্য কিছু কোড লিখছিলাম ; সময়ের সাথে পরিবর্তিত মানগুলি। এর একটি খুব সাধারণ মডেল হ'ল সময় বিন্দু থেকে সময়টির সময়কালের মান পর্যন্ত:

import Control.Applicative

-- | A History with timeline type t and value type a.
newtype History t a = History { observe :: t -> a }

instance Functor (History t) where
    -- Apply a function to the contents of a historical value
    fmap f hist = History (f . observe hist)

instance Applicative (History t) where
    -- A "pure" History is one that has the same value at all points in time
    pure = History . const

    -- This applies a function that changes over time to a value that also 
    -- changes, by observing both at the same point in time.
    ff <*> fx = History $ \t -> (observe ff t) (observe fx t)

instance Monad (History t) where
    return = pure
    ma >>= f = History $ \t -> observe (f (observe ma t)) t

Applicativeউদাহরণ হিসেবে বলা যায় মানে আপনি আছে employees :: History Day [Person]এবং customers :: History Day [Person]আপনি এটা করতে পারেন:

-- | For any given day, the list of employees followed by the customers
employeesAndCustomers :: History Day [Person]
employeesAndCustomers = (++) <$> employees <*> customers

যেমন, Functorএবং Applicativeইতিহাসের সাথে কাজ করার জন্য আমাদের নিয়মিত, অ-.তিহাসিক ফাংশনগুলি মানিয়ে নিতে অনুমতি দিন।

মনড উদাহরণটি স্বজ্ঞাতভাবে ফাংশনটি বিবেচনা করে বোঝা যায় (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c। ধরণের a -> History t bএকটি ফাংশন এমন একটি ফাংশন যা মানগুলির aইতিহাসকে মানচিত্র করে b; উদাহরণস্বরূপ, আপনি থাকতে পারে getSupervisor :: Person -> History Day Supervisor, এবং getVP :: Supervisor -> History Day VP। সুতরাং মোনাড উদাহরণটি Historyএই জাতীয় ফাংশন রচনা সম্পর্কে; উদাহরণস্বরূপ, getSupervisor >=> getVP :: Person -> History Day VPযে ফাংশনটি পায় তা হ'ল যে কোনওর জন্য তারা যা করেছে Personতার ইতিহাস VP

ঠিক আছে, এই Historyমোনাড আসলে ঠিক একই রকম ReaderHistory t aসত্যিই একই Reader t a(যা হিসাবে একই t -> a)।

আরেকটি উদাহরণ: আমি সম্প্রতি হাস্কেলের ওএলএপ ডিজাইনগুলি প্রোটোটাইপ করছি । এখানে একটি ধারণা হ'ল "হাইপারকিউব", যা মানগুলির একটি সেটগুলির ছেদ থেকে ম্যাপিং। এখানে আমরা আবার যাই:

newtype Hypercube intersection value = Hypercube { get :: intersection -> value }

হাইপারকিউবেসগুলির অপারেশনের একটি সাধারণ বিষয় হ'ল হাইপারকিউবের সংশ্লিষ্ট পয়েন্টগুলিতে মাল্টিপ্লেস স্কেলার ফাংশন প্রয়োগ করা। এটির Applicativeজন্য একটি উদাহরণ নির্ধারণ করে আমরা এটি পেতে পারি Hypercube:

instance Functor (Hypercube intersection) where
    fmap f cube = Hypercube (f . get cube)


instance Applicative (Hypercube intersection) where
    -- A "pure" Hypercube is one that has the same value at all intersections
    pure = Hypercube . const

    -- Apply each function in the @ff@ hypercube to its corresponding point 
    -- in @fx@.
    ff <*> fx = Hypercube $ \x -> (get ff x) (get fx x)

আমি কেবল Historyউপরের কোডটি কপিপাস্ট করেছি এবং নাম পরিবর্তন করেছি। যেমন আপনি বলতে পারেন, Hypercubeঠিক আছে Reader

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

  • অভিব্যক্তি = ক Reader
  • ফ্রি ভেরিয়েবল = এর ব্যবহার ask
  • মূল্যায়ন পরিবেশ = Readerকার্যকরকরণের পরিবেশ।
  • বাইন্ডিং কনস্ট্রাক্টস = local

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

সুতরাং Functor, Applicativeএবং Monadউদাহরণগুলি Readerহ'ল যে ক্ষেত্রে আপনি "একটি aঅনুপস্থিত একটি ", বাছাইয়ের যে কোনও কিছুর মডেলিং করছেন এমন ক্ষেত্রে খুব দরকারী সাধারণীকরণ rএবং আপনাকে এই "অসম্পূর্ণ" অবজেক্টগুলিকে সম্পূর্ণরূপে আচরণ করার অনুমতি দেয়।

তবুও একই কথা অন্য উপায়: একটি Reader r aএমন কিছু বিষয় যা হ্রাস হয় rএবং উৎপন্ন a, এবং Functor, Applicativeএবং Monadদৃষ্টান্ত সঙ্গে কাজ করার জন্য মৌলিক নিদর্শন হয় Readerগুলি। Functor= এমন একটি তৈরি করুন Readerযা অন্যের আউটপুট পরিবর্তন করে Reader; Applicative= Readerএকই ইনপুটটিতে দুটি গুলি সংযুক্ত করুন এবং তাদের ফলাফলগুলি একত্রিত করুন; Monad= এ এর ​​একটি ফলাফল পরিদর্শন করুন Readerএবং এটি অন্য নির্মাণে ব্যবহার করুন Readerlocalএবং withReaderফাংশন = একটি করা Readerযে মডিফাই অন্য ইনপুট Reader


5
দুর্দান্ত উত্তর। এছাড়াও আপনি ব্যবহার করতে পারেন GeneralizedNewtypeDerivingআহরণ করা এক্সটেনশন Functor, Applicative, Monadতাদের অন্তর্নিহিত ধরনের উপর ভিত্তি করে newtypes জন্য, ইত্যাদি।
রেন হেনরিচস

20

জাভা বা সি ++ এ আপনি যে কোনও সমস্যা ছাড়াই যে কোনও জায়গা থেকে কোনও পরিবর্তনশীল অ্যাক্সেস করতে পারেন। আপনার কোডটি বহু-থ্রেড হয়ে গেলে সমস্যাগুলি উপস্থিত হয়।

হাস্কেলে আপনার কাছে দুটি কার্য একটি ফাংশন থেকে অন্য ফাংশনে যাওয়ার জন্য দুটি উপায় রয়েছে:

  • আপনি কলযোগ্য ফাংশনের একটি ইনপুট প্যারামিটারের মাধ্যমে মানটি পাস করেন। ত্রুটিগুলি হ'ল: 1) আপনি সমস্ত ভেরিয়েবলগুলি সেভাবে পাস করতে পারবেন না - ইনপুট পরামিতিগুলির তালিকা কেবল আপনার মনকে উড়িয়ে দেবে। 2) ফাংশন কল ক্রমানুসারে: fn1 -> fn2 -> fn3, ফাংশন fn2প্যারামিটার যা আপনি থেকে পাস প্রয়োজন নাও হতে পারে fn1জন্য fn3
  • আপনি কিছু মোনাডের সুযোগে পাস করুন। খসড়াটি হ'ল: মোনাড ধারণাটি কী তা আপনাকে দৃ firm়ভাবে বুঝতে হবে। আশেপাশের মানগুলি পাস করা কেবলমাত্র অ্যাপ্লিকেশনগুলির মধ্যে একটি যেখানে আপনি মনড ব্যবহার করতে পারেন is আসলে মোনাদ ধারণাটি অবিশ্বাস্য শক্তিশালী। বিরক্ত হবেন না, যদি আপনি একবারে অন্তর্দৃষ্টি না পান। কেবল চেষ্টা চালিয়ে যান, এবং বিভিন্ন টিউটোরিয়াল পড়ুন। আপনি যে জ্ঞান পাবেন তা বন্ধ হয়ে যাবে।

পাঠক মনাদ ফাংশনগুলির মধ্যে ভাগ করতে চান এমন ডেটা কেবল সরিয়ে দেয়। ফাংশনগুলি সেই ডেটাটি পড়তে পারে তবে এটি পরিবর্তন করতে পারে না। রিডার মোনাদ এটাই করে। ঠিক আছে, প্রায় সব। এখানে বিভিন্ন ধরণের ক্রিয়াকলাপ রয়েছে localতবে প্রথমবারের মতো আপনি asksকেবলমাত্র আটকে থাকতে পারেন ।


3
আশেপাশে তথ্য প্রেরণের জন্য স্নাতকের ব্যবহারের আরও একটি অসুবিধা হ'ল doনোট -ইন- নোটেশনে প্রচুর 'অপরিহার্য-শৈলী' কোড লেখার পক্ষে নিজেকে খুঁজে পাওয়া খুব সহজ , যা খাঁটি ফাংশনে রিফ্যাক্টর হওয়া ভাল better
বেনিয়ামিন হজসন

4
@ বেঞ্জামিনহডসন ডোন-নোটেশনে মনড সহ 'অত্যাবশ্যক-বর্ণনামূলক' কোড লেখার অর্থ পার্শ্ব-কার্যকর (অপবিত্র) কোড লেখার প্রয়োজন নেই necessary আসলে, হাস্কেলের পার্শ্ব-কার্যকর কোড কেবল আইও মোনাডের অভ্যন্তরেই সম্ভব হতে পারে।
দিমিত্রি বেসপালভ

অন্য ফাংশনটি যদি একটি whereধারা দ্বারা একটিতে সংযুক্ত থাকে , তবে এটি ভেরিয়েবলগুলি পাস করার 3 য় উপায় হিসাবে গ্রহণ করা হবে?
Elmex80s
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.