ठोस उदाहरण दिखा रहा है कि रचना (सबूत के साथ) के तहत मठ बंद नहीं हैं?


82

यह सर्वविदित है कि एप्लाइड फंक्शनलर्स कंपोजिशन के तहत बंद हैं, लेकिन मोनाड्स नहीं हैं। हालाँकि, मुझे एक ठोस प्रतिधारण दिखाने में परेशानी हो रही है जो दिखा रहा है कि सन्यासी हमेशा रचना नहीं करते हैं।

यह उत्तर[String -> a] एक गैर-मठ के एक उदाहरण के रूप में देता है । बिट के लिए इसके साथ खेलने के बाद, मैं इसे सहज रूप से मानता हूं, लेकिन यह जवाब सिर्फ यह कहता है कि "ज्वाइन नहीं किया जा सकता है" वास्तव में कोई औचित्य दिए बिना। मैं कुछ और औपचारिक करना चाहूंगा। बेशक प्रकार के साथ बहुत सारे कार्य हैं [String -> [String -> a]] -> [String -> a]; यह दिखाना चाहिए कि इस तरह के किसी भी कार्य से मठ के कानूनों को पूरा नहीं किया जा सकता है।

कोई भी उदाहरण (प्रमाण के साथ) करेगा; मैं विशेष रूप से उपरोक्त उदाहरण के प्रमाण की तलाश में नहीं हूं।


मुझे जो सबसे करीब मिल सकता है, वह है web.cecs.pdx.edu/~mpj/pubs/RR-1004.pdf का परिशिष्ट , जो बताता है कि बहुत सी सरल मान्यताओं के तहत, joinदो साधुओं की रचना के लिए लिखना असंभव है सामान्य । लेकिन इससे कोई ठोस उदाहरण नहीं मिलता है।
ब्रेंट यॉर्गी

इस सवाल का बेहतर जवाब आपको cs.stackexchange.com, नई कंप्यूटर साइंस स्टैक एक्सचेंज साइट पर मिल सकता है।
पैट्रिक87

3
शायद मुझे समझ में नहीं आ रहा है, लेकिन मुझे लगता है कि प्रश्न को अधिक सटीक रूप से परिभाषित किया जा सकता है। जब आप दो साधुओं को "रचना" कहते हैं, तो क्या आपका मतलब केवल प्रकार के रचनाकारों की रचना करना है? और जब परिणाम "एक भिक्षु नहीं होता है", तो क्या इसका मतलब यह है कि उस प्रकार के एक संकरा उदाहरण को नहीं लिखा जा सकता है? और, यदि रचित टाइप कंस्ट्रक्टर के लिए एक मोनड इंस्टेंस लिखा जा सकता है, तो क्या यह दो कारक मोनड्स के उदाहरणों के संबंध में है, या यह पूरी तरह से असंबंधित हो सकता है?
ओवेन

1
हां, मेरा मतलब है कि प्रकार के रचनाकारों की रचना; "एक संन्यासी नहीं" का अर्थ है एक वैध (वैध) सनक उदाहरण नहीं लिखा जा सकता है; और मुझे इस बात की परवाह नहीं है कि रचना के उदाहरणों का कारकों के उदाहरणों से कोई संबंध है या नहीं।
ब्रेंट यॉर्गी

जवाबों:


42

इस इकाई जो isomorphic को है पर विचार करें (Bool ->)इकाई:

data Pair a = P a a

instance Functor Pair where
  fmap f (P x y) = P (f x) (f y)

instance Monad Pair where
  return x = P x x
  P a b >>= f = P x y
    where P x _ = f a
          P _ y = f b

और इसे Maybeमोनाड के साथ लिखें :

newtype Bad a = B (Maybe (Pair a))

मेरा दावा है कि Badवह सन्यासी नहीं हो सकता।


आंशिक प्रमाण:

fmapसंतुष्ट करने के लिए केवल एक ही तरीका है fmap id = id:

instance Functor Bad where
    fmap f (B x) = B $ fmap (fmap f) x

मठ के कानूनों को याद करें:

(1) join (return x) = x 
(2) join (fmap return x) = x
(3) join (join x) = join (fmap join x)

की परिभाषा के लिए return x, हमारे पास दो विकल्प हैं: B Nothingया B (Just (P x x))। यह स्पष्ट है कि x(1) और (2) से लौटने की कोई उम्मीद नहीं है, हम फेंक नहीं सकते x, इसलिए हमें दूसरा विकल्प चुनना होगा।

return' :: a -> Bad a
return' x = B (Just (P x x))

वह निकल जाता है join। चूंकि कुछ ही संभावित इनपुट हैं, हम प्रत्येक के लिए एक मामला बना सकते हैं:

join :: Bad (Bad a) -> Bad a
(A) join (B Nothing) = ???
(B) join (B (Just (P (B Nothing)          (B Nothing))))          = ???
(C) join (B (Just (P (B (Just (P x1 x2))) (B Nothing))))          = ???
(D) join (B (Just (P (B Nothing)          (B (Just (P x1 x2)))))) = ???
(E) join (B (Just (P (B (Just (P x1 x2))) (B (Just (P x3 x4)))))) = ???

चूंकि आउटपुट में टाइप होता है Bad a, केवल विकल्प होते हैं B Nothingया B (Just (P y1 y2))जहां y1, y2को चुनना होता है x1 ... x4

मामलों (ए) और (बी) में, हमारे पास कोई प्रकार का मान नहीं है a, इसलिए हम B Nothingदोनों मामलों में लौटने के लिए मजबूर हैं ।

केस (E) का निर्धारण (1) और (2) मोनड कानूनों से होता है:

-- apply (1) to (B (Just (P y1 y2)))
join (return' (B (Just (P y1 y2))))
= -- using our definition of return'
join (B (Just (P (B (Just (P y1 y2))) (B (Just (P y1 y2))))))
= -- from (1) this should equal
B (Just (P y1 y2))

आदेश वापस करने के B (Just (P y1 y2))मामले (ई) में, इसका मतलब है कि हम चुनना चाहिए y1या तो से x1या x3, और y2से या तो x2या x4

-- apply (2) to (B (Just (P y1 y2)))
join (fmap return' (B (Just (P y1 y2))))
= -- def of fmap
join (B (Just (P (return y1) (return y2))))
= -- def of return
join (B (Just (P (B (Just (P y1 y1))) (B (Just (P y2 y2))))))
= -- from (2) this should equal
B (Just (P y1 y2))

इसी तरह, यह कहना है कि हम चुनना चाहिए y1या तो से x1या x2, और y2से या तो x3या x4। दोनों को मिलाकर, हम यह निर्धारित करते हैं कि (ई) का दाहिना हाथ होना चाहिए B (Just (P x1 x4))

अब तक यह सब अच्छा है, लेकिन समस्या तब आती है जब आप (सी) और (डी) के लिए दाहिने हाथ की तरफ भरने की कोशिश करते हैं।

प्रत्येक के लिए 5 संभव दाएं हाथ हैं, और कोई भी संयोजन काम नहीं करता है। मेरे पास अभी तक इसके लिए एक अच्छा तर्क नहीं है, लेकिन मेरे पास एक ऐसा कार्यक्रम है जो सभी संयोजनों का विस्तृत परीक्षण करता है:

{-# LANGUAGE ImpredicativeTypes, ScopedTypeVariables #-}

import Control.Monad (guard)

data Pair a = P a a
  deriving (Eq, Show)

instance Functor Pair where
  fmap f (P x y) = P (f x) (f y)

instance Monad Pair where
  return x = P x x
  P a b >>= f = P x y
    where P x _ = f a
          P _ y = f b

newtype Bad a = B (Maybe (Pair a))
  deriving (Eq, Show)

instance Functor Bad where
  fmap f (B x) = B $ fmap (fmap f) x

-- The only definition that could possibly work.
unit :: a -> Bad a
unit x = B (Just (P x x))

-- Number of possible definitions of join for this type. If this equals zero, no monad for you!
joins :: Integer
joins = sum $ do
  -- Try all possible ways of handling cases 3 and 4 in the definition of join below.
  let ways = [ \_ _ -> B Nothing
             , \a b -> B (Just (P a a))
             , \a b -> B (Just (P a b))
             , \a b -> B (Just (P b a))
             , \a b -> B (Just (P b b)) ] :: [forall a. a -> a -> Bad a]
  c3 :: forall a. a -> a -> Bad a <- ways
  c4 :: forall a. a -> a -> Bad a <- ways

  let join :: forall a. Bad (Bad a) -> Bad a
      join (B Nothing) = B Nothing -- no choice
      join (B (Just (P (B Nothing) (B Nothing)))) = B Nothing -- again, no choice
      join (B (Just (P (B (Just (P x1 x2))) (B Nothing)))) = c3 x1 x2
      join (B (Just (P (B Nothing) (B (Just (P x3 x4)))))) = c4 x3 x4
      join (B (Just (P (B (Just (P x1 x2))) (B (Just (P x3 x4)))))) = B (Just (P x1 x4)) -- derived from monad laws

  -- We've already learnt all we can from these two, but I decided to leave them in anyway.
  guard $ all (\x -> join (unit x) == x) bad1
  guard $ all (\x -> join (fmap unit x) == x) bad1

  -- This is the one that matters
  guard $ all (\x -> join (join x) == join (fmap join x)) bad3

  return 1 

main = putStrLn $ show joins ++ " combinations work."

-- Functions for making all the different forms of Bad values containing distinct Ints.

bad1 :: [Bad Int]
bad1 = map fst (bad1' 1)

bad3 :: [Bad (Bad (Bad Int))]
bad3 = map fst (bad3' 1)

bad1' :: Int -> [(Bad Int, Int)]
bad1' n = [(B Nothing, n), (B (Just (P n (n+1))), n+2)]

bad2' :: Int -> [(Bad (Bad Int), Int)]
bad2' n = (B Nothing, n) : do
  (x, n')  <- bad1' n
  (y, n'') <- bad1' n'
  return (B (Just (P x y)), n'')

bad3' :: Int -> [(Bad (Bad (Bad Int)), Int)]
bad3' n = (B Nothing, n) : do
  (x, n')  <- bad2' n
  (y, n'') <- bad2' n'
  return (B (Just (P x y)), n'')

धन्यवाद, मैं आश्वस्त हूं! हालांकि यह मुझे आश्चर्यचकित करता है कि क्या आपके प्रमाण को सरल बनाने के तरीके हैं।
ब्रेंट यॉर्गी

1
@BentYorgey: मुझे संदेह है कि वहाँ होना चाहिए, क्योंकि मामलों (सी) और (डी) के साथ समस्याओं को परिभाषित करने की कोशिश करते समय आपके द्वारा की जाने वाली समस्याओं की तरह बहुत अधिक लगता है swap :: Pair (Maybe a) -> Maybe (Pair a)
हमर

11
तो, संक्षेप में: भिक्षुओं को जानकारी फेंकने की अनुमति है, और यह ठीक है अगर वे सिर्फ अपने आप में नेस्टेड हैं। लेकिन जब आपके पास एक सूचना-संरक्षण करने वाला मोनाड और एक सूचना छोड़ने वाला मोनाड होता है, तो दो बूंदों की जानकारी को मिलाते हुए, भले ही जानकारी-संरक्षण करने वाले को उस जानकारी की आवश्यकता होती है जो अपने स्वयं के मोनाड कानूनों को संतुष्ट करने के लिए रखी जाती है। इसलिए आप मनमाने तरीके से भिक्षुओं को जोड़ नहीं सकते। (यही कारण है कि आपको ट्रैवर्सेबल मोनैड्स की आवश्यकता है, जो गारंटी देते हैं कि वे प्रासंगिक जानकारी को नहीं छोड़ेंगे;
Xanthir

@Xanthir कम्पोज़िंग केवल एक क्रम में काम करती है: (Maybe a, Maybe a)एक सन्यासी है (क्योंकि यह दो संन्यासी का एक उत्पाद है) लेकिन Maybe (a, a)एक सन्यासी नहीं है। मैंने यह भी सत्यापित किया है कि Maybe (a,a)स्पष्ट गणना से कोई सन्यासी नहीं है।
winitzki

मन दिखा रहा Maybe (a, a)है कि एक सन्यासी क्यों नहीं है? हो सकता है कि दोनों और टपल ट्रैवर्सेबल हों, और किसी भी क्रम में रचना करने योग्य हों; अन्य एसओ प्रश्न हैं जो इस विशिष्ट उदाहरण के बारे में भी बात करते हैं।
ज़ांथिर

38

एक छोटे ठोस प्रतिधारण के लिए, टर्मिनल मोनड पर विचार करें।

data Thud x = Thud

returnऔर >>=बस जाओ Thud, और कानून तुच्छता से पकड़ो।

अब हमारे पास बूल के लिए लेखक का सनद भी है (चलो, कहते हैं, ज़ार-मोनॉइड संरचना)।

data Flip x = Flip Bool x

instance Monad Flip where
   return x = Flip False x
   Flip False x  >>= f = f x
   Flip True x   >>= f = Flip (not b) y where Flip b y = f x

एर, उम, हमें रचना की आवश्यकता होगी

newtype (:.:) f g x = C (f (g x))

अब परिभाषित करने की कोशिश ...

instance Monad (Flip :.: Thud) where  -- that's effectively the constant `Bool` functor
  return x = C (Flip ??? Thud)
  ...

पैरामीट्रिकिटी हमें बताती है कि ???किसी भी उपयोगी तरीके पर निर्भर नहीं हो सकता है x, इसलिए यह एक स्थिर होना चाहिए। नतीजतन, join . returnआवश्यक रूप से एक स्थिर कार्य भी है, इसलिए कानून

join . return = id

जो कुछ भी परिभाषा के लिए असफल चाहिए joinऔरreturn हम चुनते ।


3
कार्लो के हमालैनेन ब्लॉग पर उपरोक्त उत्तर का अतिरिक्त, बहुत स्पष्ट और विस्तृत विश्लेषण है जो मुझे मददगार लगा है: carlo-hamalainen.net/blog/2014/1/2/…
paluh

34

बीच में छोड़कर निर्माण करना

(->) rहर के लिए एक सन्यासी है rऔर हर के लिए Either eएक सन्यासी है e। आइए उनकी संरचना को परिभाषित करें ( (->) rअंदर, Either eबाहर):

import Control.Monad
newtype Comp r e a = Comp { uncomp :: Either e (r -> a) }

मेरा दावा है कि अगर Comp r eहर एक के लिए एक सन्यासी थे rऔर eतब हमें निर्वासित मध्य के कानून का एहसास हो सकता था । यह अंतर्ज्ञानवादी तर्क में संभव नहीं है जो कार्यात्मक भाषाओं के प्रकारों को रेखांकित करता है (बहिष्कृत मध्य का कानून कॉल / सीसी ऑपरेटर होने के बराबर है )।

मान Compलेते हैं कि एक साधु है। तो हमारे पास हैं

join :: Comp r e (Comp r e a) -> Comp r e a

और इसलिए हम परिभाषित कर सकते हैं

swap :: (r -> Either e a) -> Either e (r -> a)
swap = uncomp . join . Comp . return . liftM (Comp . liftM return)

(यह swapकागज कम्पोजिंग मोनडेस से सिर्फ फ़ंक्शन है जिसमें ब्रेंट का उल्लेख है। संप्रदाय। 4.3, केवल newtype's (डी) कंस्ट्रक्टर्स के साथ जोड़ा गया है। ध्यान दें कि हमें परवाह नहीं है कि इसमें क्या गुण हैं, केवल महत्वपूर्ण बात यह है कि यह निश्चित और कुल है। ।)

अब सेट करते हैं

data False -- an empty datatype corresponding to logical false
type Neg a = (a -> False) -- corresponds to logical negation

और के लिए स्वैप विशेषज्ञ r = b, e = b, a = False:

excludedMiddle :: Either b (Neg b)
excludedMiddle = swap Left

निष्कर्ष: भले ही (->) rऔर Either rमोनड हैं, उनकी रचना Comp r rनहीं हो सकती है।

नोट: कि यह भी कैसे ReaderTऔर EitherTपरिभाषित किया गया है में परिलक्षित होता है । दोनों ReaderT r (Either e) और EitherT e (Reader r)समरूप हैं r -> Either e a! दोहरे के लिए मोनाड को परिभाषित करने का कोई तरीका नहीं है Either e (r -> a)


भागने IO क्रिया

एक ही नस में कई उदाहरण होते हैं जिनमें शामिल होते हैं IOऔर जो IOकिसी तरह बच निकलते हैं । उदाहरण के लिए:

newtype Comp r a = Comp { uncomp :: IO (r -> a) }

swap :: (r -> IO a) -> IO (r -> a)
swap = uncomp . join . Comp . return . liftM (Comp . liftM return)

अब चलो

main :: IO ()
main = do
   let foo True  = print "First" >> return 1
       foo False = print "Second" >> return 2
   f <- swap foo
   input <- readLn
   print (f input)

जब यह कार्यक्रम चलाया जाएगा तो क्या होगा? दो संभावनाएँ हैं:

  1. कंसोल से पढ़ने के बाद "प्रथम" या "दूसरा" मुद्रित inputकिया जाता है, जिसका अर्थ है कि क्रियाओं का क्रम उलट गया था और यह कि क्रियाएं fooशुद्ध में बच गईं f
  2. या swap(इसलिए join) IOकार्रवाई को दूर फेंकता है और न तो "पहले" और न ही "दूसरा" कभी मुद्रित होता है। लेकिन इसका मतलब यह है कि joinकानून का उल्लंघन करता है

    join . return = id
    

    क्योंकि अगर कार्रवाई दूर joinफेंकता IOहै, तो

    foo ≠ (join . return) foo
    

अन्य समान IO+ मोनाड संयोजन निर्माण का नेतृत्व करते हैं

swapEither :: IO (Either e a) -> Either e (IO a)
swapWriter :: (Monoid e) => IO (Writer e a) -> Writer e (IO a)
swapState  :: IO (State e a) -> State e (IO a)
...

या तो उनके joinकार्यान्वयन eसे बचने की अनुमति देनी चाहिए IOया उन्हें इसे फेंक देना चाहिए और कानून का उल्लंघन करते हुए कुछ और के साथ प्रतिस्थापित करना चाहिए।


(मुझे लगता है कि "एपी" "एक टाइपो है, जहां फाम, शुद्ध और एपी विहित परिभाषाएं हैं" ( <*>इसके बजाय होना चाहिए ), इसे संपादित करने की कोशिश की गई, लेकिन मुझे बताया गया कि मेरा संपादन बहुत छोटा था।) --- यह स्पष्ट नहीं है मुझे लगता है कि एक परिभाषा होने के लिए एक परिभाषा का joinमतलब है swap। क्या आप इस पर विस्तार कर सकते हैं? कागज ब्रेंट द्वारा संदर्भित सूचित करते हैं कि से जाने के लिए लगता है joinके लिए swapहम निम्न मान्यताओं की जरूरत है: joinM . fmapM join = join . joinMऔर join . fmap (fmapM joinN ) = fmapM joinN . join जहां joinM = में शामिल होने :: एम, आदि
राफेल कैटानो

1
@ RafaelCaetano टाइपो के लिए धन्यवाद, मैंने इसे ठीक कर दिया (और swapपेपर का मिलान करने के लिए फ़ंक्शन का नाम भी बदल दिया )। मैंने अब तक कागज की जांच नहीं की, और आप सही हैं, ऐसा लगता है कि हमें swap<-> को परिभाषित करने के लिए J (1) और J (2) की आवश्यकता है join। यह शायद मेरे प्रमाण का एक कमजोर स्थान है और मैं इसके बारे में अधिक सोचूंगा (शायद यह इस तथ्य से कुछ प्राप्त करना संभव होगा कि यह है Applicative)।
पेट्र

@ RafaelCaetano लेकिन मेरा मानना ​​है कि प्रमाण अभी भी मान्य है: यदि हमारे पास था join, तो हम swap :: (Int -> Maybe a) -> Maybe (Int -> a)उपरोक्त परिभाषा का उपयोग कर परिभाषित कर सकते हैं (कोई बात नहीं जो इस कानून को swapसंतुष्ट करता है)। ऐसा swapव्यवहार कैसे होगा ? नहीं होने के बाद Int, इसके पास अपने तर्क को पारित करने के लिए कुछ भी नहीं है, इसलिए इसे Nothingसभी निविष्टियों के लिए वापस लौटना होगा । मेरा मानना ​​है कि हम पीछे से joinपरिभाषित करने की आवश्यकता के बिना मठ के कानूनों के लिए विरोधाभास प्राप्त कर सकते हैं । मैं इसकी जाँच करूँगा और आपको बता दूँगा। joinswap
पेट्र

@Petr: जैसा कि यह खड़ा है मैं राफेल से सहमत हूं कि यह काफी प्रमाण नहीं है जिसकी मुझे तलाश है, लेकिन मैं यह देखने के लिए उत्सुक हूं कि क्या यह आपके द्वारा उल्लिखित लाइनों के साथ तय किया जा सकता है।
ब्रेंट यॉर्गी

1
@ PetrPudlák वाह, बहुत अच्छा! हां, मैं अब इसे पूरी तरह से खरीदता हूं। ये कुछ वास्तव में दिलचस्प अंतर्दृष्टि हैं। मैंने अनुमान नहीं लगाया होगा कि बस स्वैप का निर्माण करने में सक्षम होने से विरोधाभास पैदा हो सकता है, बिना किसी मोनाड कानूनों के संदर्भ में! यदि मैं कई उत्तर स्वीकार कर सकता हूं तो मैं इसे भी स्वीकार करूंगा।
ब्रेंट यॉर्गी

4

आपका लिंक इस डेटा प्रकार को संदर्भित करता है, तो आइए कुछ विशिष्ट कार्यान्वयन चुनने की कोशिश करें: data A3 a = A3 (A1 (A2 a))

मैं मनमानी करूंगा A1 = IO, A2 = []। हम इसे भी बनाएंगे newtypeऔर इसे मज़े के लिए विशेष रूप से बताया गया नाम देंगे:

newtype ListT IO a = ListT (IO [a])

आइए उस प्रकार की कुछ मनमानी कार्रवाई करते हैं, और इसे दो अलग-अलग तरीकों से चलाते हैं:

λ> let v n = ListT $ do {putStr (show n); return [0, 1]}
λ> runListT $ ((v >=> v) >=> v) 0
0010101[0,1,0,1,0,1,0,1]
λ> runListT $ (v >=> (v >=> v)) 0
0001101[0,1,0,1,0,1,0,1]

जैसा कि आप देख सकते हैं, यह संबद्धता कानून को तोड़ता है ∀x y z. (x >=> y) >=> z == x >=> (y >=> z):।

यह पता चला, ListT mकेवल एक इकाई है अगर mएक है विनिमेय इकाई। यह भिक्षुओं के एक बड़े वर्ग को रचना करने से रोकता है [], जो "दो मनमाने साधुओं की रचना एक भिक्षु की रचना" के सार्वभौमिक नियम को तोड़ता है।

इसे भी देखें: https://stackoverflow.com/a/12617918/1769569


11
मुझे लगता है कि यह केवल दिखाता है कि एक विशिष्ट परिभाषा ListTसभी मामलों में एक सनक का उत्पादन करने में विफल रहती है, बजाय यह दिखाने के कि कोई भी संभावित परिभाषा काम नहीं कर सकती है।
सीए मैककैन

मुझे नहीं करना है। "इस सब के लिए," यह "वहां" एक काउंटर-उदाहरण मौजूद है। प्रश्न पूछा गया था "सभी साधुओं के लिए, उनकी रचना एक मठ बनाती है"। मैंने उन प्रकारों का एक संयोजन दिखाया है जो अपने आप में भिक्षु हैं, लेकिन रचना नहीं कर सकते।
एचपीसी

11
@hpc, लेकिन दो साधुओं की रचना उनके प्रकारों की रचना से अधिक है। आपको संचालन की भी आवश्यकता है, और ब्रेंट के सवाल की मेरी व्याख्या यह है कि संचालन के कार्यान्वयन को प्राप्त करने का एक तरीका नहीं हो सकता है - वह अभी तक कुछ मजबूत देख रहा है, कि कुछ रचनाओं में कोई संचालन नहीं है जो कानूनों को संतुष्ट करता है, चाहे यंत्रवत् व्युत्पन्न या नहीं। क्या इसका कोई मतलब है?
लुकी

हां, लूकी के पास यह अधिकार है। क्षमा करें यदि मेरा मूल प्रश्न स्पष्ट नहीं था।
ब्रेंट यॉर्गी

इस उत्तर से वास्तव में क्या गायब है, Monadउदाहरण के लिए ListT, और एक प्रदर्शन है कि कोई अन्य नहीं हैं। बयान "इस सब के लिए है, वहां मौजूद है" और इसलिए नकारात्मकता है "इस तरह से मौजूद है कि सभी के लिए"
बेन मिलवुड

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.