हास्केल क्विकबैच: जंकलिस्ट मोनोइड का परीक्षण mconcat पर स्टैक ओवरफ्लो में होता है
मैं 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
जवाब
हां, त्रुटि के कारण है pure mempty
, लेकिन इसका मतलब यह नहीं है pure mempty
कि गलत है। पहले वहाँ देखते हैं।
यह परिभाषा में शामिल प्रकारों को देखने में बहुत मदद करता है mempty = pure mempty
:
mempty :: ZipList a
mempty = (pure :: a -> ZipList a) (mempty :: a)
मूल रूप से, हम pure
एक प्रकार का ZipList
आउट बनाने के लिए ऑपरेशन का उपयोग करने जा रहे हैं । यह को देखने के लिए यहां से मदद करता है की परिभाषा के लिए :mempty
a
pureZipList
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। इस बारे में कुछ भी अनंत नहीं होगा। - तीसरे मामले में, हम
mconcat
ZipLists की सूची तैयार कर रहे हैं और सूची का उल्लेख कर रहे हैं । लेकिन, सूची खाली होने पर क्या होता है? खैर,mconcat [] = mempty
और एक खाली सूची को फोल्ड करने से उत्पादन होता हैmempty
। इसका मतलब है, यदि खाली सूची को मनमाना इनपुट (जो पूरी तरह से संभव है) के रूप में उत्पन्न होता है, तो परीक्षण यह पुष्टि करने की कोशिश करेगा कि एक अनंत सूची एक अन्य अनंत सूची के बराबर है, जिसके परिणामस्वरूप हमेशा स्टैक ओवरफ्लो या ब्लैक होल होगा।
आप इसे कैसे ठीक कर सकते हैं? मैं दो तरीकों से आ सकता हूं:
आप के स्वयं के संस्करण परिभाषित कर सकते हैं
EqProp
के लिएZipList
इतना है कि यह केवल सूची में से कुछ परिमित उपसर्ग पर समानता तुलना करती है। यह संभवतः एक नया स्वरूप बनाने वाला होगा (शायदnewtype MonZipList a = MonZipList (ZipList a)
), उदाहरणों का एक गुच्छा प्राप्त करना, और फिरEqProp
हाथ से लिखना । यह शायद काम करेगा लेकिन थोड़ा असभ्य है।आप अपने खुद के संस्करण को लिख सकते हैं
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
जाहिर है, यह थोड़ी कमजोर स्थिति है, लेकिन कम से कम यह एक है जो लटका नहीं होगा।