हास्केल क्विकबैच: जंकलिस्ट मोनोइड का परीक्षण mconcat पर स्टैक ओवरफ्लो में होता है

Jan 14 2021

मैं ZipList सेमीग्रुप और Monoid के लिए अनाथ उदाहरण बनाया है। हालाँकि, जब मैं क्वॉंकबैच से टेस्ट को मोनोकैट पर चलाता हूं, तो mconcat टेस्ट में, स्टैक ओवरफ्लो त्रुटि होती है। मैं इस त्रुटि को कैसे हल करूं? ऐसी त्रुटि क्यों है? क्या यह उस कारण के कारण है pure mempty, जो मुझे समझ में नहीं आता है क्योंकि मुझे यह ज्यादातर हास्केलबुक अध्याय 17 के आवेदन अनुभाग 17.8 जिपलाइन मोनॉयड से मिला है?

zl :: ZipList (Sum Int)
zl = ZipList [1,1 :: Sum Int]
instance Semigroup a 
  => Semigroup (ZipList a) where
    (<>) = liftA2 (<>)
instance (Eq a, Monoid a)
  => Monoid (ZipList a) where
    mempty = pure mempty 
    mappend = (<>)
    mconcat as = 
      foldr mappend mempty as
main :: IO ()
main = do 
  quickBatch $ monoid zl

जवाब

1 DDub Jan 18 2021 at 01:54

हां, त्रुटि के कारण है pure mempty, लेकिन इसका मतलब यह नहीं है pure memptyकि गलत है। पहले वहाँ देखते हैं।

यह परिभाषा में शामिल प्रकारों को देखने में बहुत मदद करता है mempty = pure mempty:

mempty :: ZipList a
mempty = (pure :: a -> ZipList a) (mempty :: a)

मूल रूप से, हम pureएक प्रकार का ZipListआउट बनाने के लिए ऑपरेशन का उपयोग करने जा रहे हैं । यह को देखने के लिए यहां से मदद करता है की परिभाषा के लिए :memptyapureZipList

pure :: a -> ZipList a
pure x = ZipList (repeat x)

कुल में, memptyके लिए ZipList aएक होने जा रहा है ZipListकी असीम दोहरा सूची से युक्त memptyअंतर्निहित प्रकार के मूल्यों a


इस त्रुटि पर वापस आ रहे हैं। जब आप परीक्षण चलाने का प्रयास monoidअधिक ZipList (Sum Int), QuickCheck संपत्तियों की एक अनुक्रम का परीक्षण करने जा रहा है।

  • पहले दो बाएं पहचान और सही पहचान गुणों की जांच करते हैं। ये क्या करते हैं प्रकार के मान उत्पन्न करते हैं x :: ZipList (Sum Int)और सत्यापित करते हैं x <> mempty = mempty <> x = x
  • तीसरा चेक करता है कि किन्हीं दो मूल्यों के लिए x, y :: ZipList (Sum Int), हमारे पास वह x मैप्पेंड है y = x <> y
  • चौथा जाँच करता है कि मूल्यों की किसी भी सूची के लिए x :: [ZipList (Sum Int)], इनको फोल्ड mappendकरना उसी के समान mconcatहै।

इससे पहले कि मैं जारी रखता हूं, यह ध्यान रखना महत्वपूर्ण है कि जब मैं "किसी भी मूल्य के लिए" कहता हूं, तो मेरा वास्तव में मतलब है कि क्विकचेक Arbitraryउस प्रकार के मूल्यों को उत्पन्न करने के लिए उक्त प्रकार के उदाहरण का उपयोग कर रहा है । इसके अलावा, Arbitraryके लिए उदाहरण के ZipList aरूप में ही है Arbitraryके लिए उदाहरण [a]है, लेकिन फिर में लिपटे ZipList। अंत में, Arbitraryउदाहरण के लिए [a]कभी भी अनंत सूची नहीं बनेगी (क्योंकि जब आप समानता की जाँच कर रहे हैं तो समस्याएँ पैदा होंगी, जैसे कि अनंत लूप में जाना या स्टैक से बह निकलना), इसलिए ये "किसी भी प्रकार के मूल्यों" के लिए ZipList (Sum Int)कभी भी अनंत नहीं होंगे। या तो।

विशेष रूप से, इसका मतलब है कि क्विकचेक कभी भी मनमाने ढंग से मूल्य उत्पन्न नहीं करेगा mempty :: ZipList aक्योंकि यह एक अनंत सूची है।


तो पहले 3 पास क्यों होते हैं लेकिन आखिरी एक ढेर अतिप्रवाह के साथ विफल हो जाता है? पहले तीन परीक्षणों में, हम कभी भी अनंत सूची की अनंत सूची से तुलना करने की कोशिश नहीं करते। आइए देखें क्यों नहीं।

  • पहले दो परीक्षणों में, हम देख रहे हैं x <> mempty == xऔर mempty <> x == x। दोनों मामलों में, xहमारे "मनमाने" मूल्यों में से एक है, जो कभी भी अनंत नहीं होगा, इसलिए यह समानता कभी भी अनंत लूप में नहीं जाएगी।
  • तीसरे टेस्ट में, हम दो परिमित ZipLists जेनरेट कर रहे हैं xऔर yऔर mappendउन्हें एक साथ ing। इस बारे में कुछ भी अनंत नहीं होगा।
  • तीसरे मामले में, हम mconcatZipLists की सूची तैयार कर रहे हैं और सूची का उल्लेख कर रहे हैं । लेकिन, सूची खाली होने पर क्या होता है? खैर, mconcat [] = memptyऔर एक खाली सूची को फोल्ड करने से उत्पादन होता है mempty। इसका मतलब है, यदि खाली सूची को मनमाना इनपुट (जो पूरी तरह से संभव है) के रूप में उत्पन्न होता है, तो परीक्षण यह पुष्टि करने की कोशिश करेगा कि एक अनंत सूची एक अन्य अनंत सूची के बराबर है, जिसके परिणामस्वरूप हमेशा स्टैक ओवरफ्लो या ब्लैक होल होगा।

आप इसे कैसे ठीक कर सकते हैं? मैं दो तरीकों से आ सकता हूं:

  1. आप के स्वयं के संस्करण परिभाषित कर सकते हैं EqPropके लिए ZipListइतना है कि यह केवल सूची में से कुछ परिमित उपसर्ग पर समानता तुलना करती है। यह संभवतः एक नया स्वरूप बनाने वाला होगा (शायद newtype MonZipList a = MonZipList (ZipList a)), उदाहरणों का एक गुच्छा प्राप्त करना, और फिर EqPropहाथ से लिखना । यह शायद काम करेगा लेकिन थोड़ा असभ्य है।

  2. आप अपने खुद के संस्करण को लिख सकते हैं monoidजो चौथे परीक्षण के एक अलग संस्करण का उपयोग करता है। उदाहरण के लिए, यदि आप इसे प्रतिबंधित करते हैं ताकि परीक्षण केवल गैर-रिक्त सूचियों का उपयोग करता है, तो आपको कोई समस्या नहीं होगी। ऐसा करने के लिए, आपको संपत्ति परीक्षणों की परिभाषा कोmonoid देखकर शुरू करना चाहिए । ध्यान दें कि यह वर्तमान में "mconcat" संपत्ति को परिभाषित करता है property mconcatPजहां

mconcatP :: [a] -> Property
mconcatP as = mconcat as =-= foldr mappend mempty as

क्विकचेक की अपनी NonEmptyListकक्षा का उपयोग करते हुए , आप इसे अपने उद्देश्यों के लिए फिर से लिख सकते हैं:

mconcatP :: NonEmptyList a -> Property
mconcatP (NonEmptyList as) = mconcat as =-= foldr mappend mempty as

जाहिर है, यह थोड़ी कमजोर स्थिति है, लेकिन कम से कम यह एक है जो लटका नहीं होगा।