2016-01-21 7 views
10

मैं सिर्फ written a function (Data.Sequence के लिए)मैं आवेदनों पर पॉलिमॉर्फिक कार्यों का परीक्षण कैसे कर सकता हूं?

traverseWithIndex :: Applicative f => (Int -> a -> f b) -> Seq a -> f (Seq b) 

जो

traverseWithIndex f = sequenceA . mapWithIndex f 

शुक्र का पालन करना चाहिए है, इस mapWithIndex के स्रोत का एक सीधा यांत्रिक संशोधन है, इसलिए मैं यह सही है काफी आश्वस्त हूँ । हालांकि, अधिक जटिल मामलों में पूरी तरह से परीक्षण की आवश्यकता होगी। मैं इस सरल एक का परीक्षण करने के लिए एक त्वरित जांच संपत्ति लिखने की कोशिश कर रहा हूं। जाहिर है, मैं इसे हर Applicative मज़ेदार के साथ कोशिश नहीं कर सकता! मोनोइड्स का परीक्षण करते समय, यह किसी प्रकार के मुक्त मोनोइड (यानी, सीमित सूचियों) के साथ परीक्षण करने के लिए अच्छी समझ में आता है। तो कुछ मज़ेदार पर free applicative functor के साथ परीक्षण करने के लिए यहां समझदार लगता है। दो कठिनाइयां हैं:

  1. मैं एक उपयुक्त बेस फ़ैक्टर कैसे चुनूं? मैं शायद एक बुराई चाहता हूं जो आवेदक या ट्रैवर्सबल या कुछ भी नहीं है, लेकिन ऐसी चीज के साथ काम करना मुश्किल लगता है।

  2. मैं परिणामों की तुलना कैसे करूं? उनके पास कार्य होंगे, इसलिए उनके पास Eq उदाहरण नहीं है।

उत्तर

3

जाहिर है, मैं इसे बाहर की कोशिश नहीं कर सकते हैं कि हर Applicative functor के साथ!

मैं इस ब्लॉग पोस्ट श्रृंखला है, जो मैं पूरी तरह से करने के लिए दावा नहीं होगा की याद दिला रहा हूँ समझ में:

सबक है कि मैं से ड्राइंग याद यह है कि जंगली में आप देखे जाने वाले लगभग हर आवेदक मज़ेदार को सरल, जैसे कि (पूर्ण होने के लिए नहीं) की रचना, उत्पाद या (प्रतिबंधित) प्रजनन होता है:

  1. Const
  2. Identity
  3. (->)

इसलिए जब आप इसे बाहर की कोशिश नहीं कर सकते हैं कि हर Applicative functor के साथ, वहाँ आगमनात्मक तर्क है कि आप करने के लिए QuickCheck गुण में फायदा उठाने के लिए सक्षम हो सकता है कर रहे हैं विश्वास प्राप्त करें कि आपका कार्य मज़दूरों के बड़े अपरिवर्तनीय परिभाषित परिवारों के लिए काम करता है। तो उदाहरण के लिए आप परीक्षण कर सकते हैं:

  • आपका फ़ंक्शन आपकी पसंद के "परमाणु" आवेदकों के लिए सही तरीके से काम करता है;
  • यदि आपका फ़ंक्शन मज़ेदार f और g के लिए सही तरीके से काम करता है, तो यह 10, Product f g और Coproduct f g के लिए सही ढंग से काम करता है।

मैं परिणामों की तुलना कैसे करूं?उनके पास कार्य होंगे, इसलिए उनके पास Eq उदाहरण नहीं है।

ठीक है, मुझे लगता है कि आपको फ़ंक्शन समानता के क्विक चेक परीक्षण को देखना पड़ सकता है। पिछली बार मुझे उन पंक्तियों के साथ कुछ करना पड़ा था, मैं कोनाल की checkers लाइब्रेरी के साथ गया था, जिसमें an EqProp class "मूल्यों के" [टी] ypes के लिए है, जो समानता के लिए परीक्षण किया जा सकता है, शायद यादृच्छिक नमूनाकरण के माध्यम से। " यह आपको पहले से ही एक विचार देना चाहिए-भले ही आपके पास Eq फ़ंक्शन के लिए उदाहरण न हो, QuickCheck यह साबित करने में सक्षम हो सकता है कि दो कार्य असमान हैं। गंभीर, इस उदाहरण मौजूद है:

instance (Show a, Arbitrary a, EqProp b) => EqProp (a -> b) 

... और किसी भी प्रकार के एक Eq उदाहरण है कि एक छोटी सी EqProp उदाहरण जहां (=-=) = (==) है।

तो यह मेरे दिमाग में, Coyoneda Something का उपयोग आधार मज़ेदार के रूप में करता है, और यह पता लगाता है कि सभी छोटे कार्यों को कैसे प्लग करना है।

+0

ऊह, चीजों को पढ़ने के लिए। मुझे कल कोशिश करनी होगी! यह बीजगणितीय दृष्टिकोण ध्वनि वादा करता है। – dfeuer

3

यहां एक आंशिक (?) समाधान है। मुख्य पहलुओं को हम जांचना चाहते हैं 1) स्पष्ट रूप से वही मान गणना की जाती है, और 2) प्रभाव एक ही क्रम में किए जाते हैं। मुझे लगता है कि निम्नलिखित कोड स्वतः स्पष्ट पर्याप्त है:

{-# LANGUAGE FlexibleInstances #-} 
module Main where 
import Control.Applicative 
import Control.Applicative.Free 
import Data.Foldable 
import Data.Functor.Identity 
import Test.QuickCheck 
import Text.Show.Functions -- for Show instance for function types 

data Fork a = F a | G a deriving (Eq, Show) 

toIdentity :: Fork a -> Identity a 
toIdentity (F a) = Identity a 
toIdentity (G a) = Identity a 

instance Functor Fork where 
    fmap f (F a) = F (f a) 
    fmap f (G a) = G (f a) 

instance (Arbitrary a) => Arbitrary (Fork a) where 
    arbitrary = elements [F,G] <*> arbitrary 

instance (Arbitrary a) => Arbitrary (Ap Fork a) where 
    arbitrary = oneof [Pure <$> arbitrary, 
         Ap <$> (arbitrary :: Gen (Fork Int)) <*> arbitrary] 

effectOrder :: Ap Fork a -> [Fork()] 
effectOrder (Pure _) = [] 
effectOrder (Ap x f) = fmap (const()) x : effectOrder f 

value :: Ap Fork a -> a 
value = runIdentity . runAp toIdentity 

checkApplicative :: (Eq a) => Ap Fork a -> Ap Fork a -> Bool 
checkApplicative x y = effectOrder x == effectOrder y && value x == value y 

succeedingExample = quickCheck (\f x -> checkApplicative 
    (traverse (f :: Int -> Ap Fork Int) (x :: [Int])) 
    (sequenceA (fmap f x))) 

-- note reverse 
failingExample = quickCheck (\f x -> checkApplicative 
    (traverse (f :: Int -> Ap Fork Int) (reverse x :: [Int])) 
    (sequenceA (fmap f x))) 

-- instance just for example, could make a more informative one 
instance Show (Ap Fork Int) where show _ = "<Ap>" 

-- values match ... 
betterSucceedingExample = quickCheck (\x -> 
    value (sequenceA (x :: [Ap Fork Int])) 
== value (fmap reverse (sequenceA (reverse x)))) 

-- but effects don't. 
betterFailingExample = quickCheck (\x -> checkApplicative 
    (sequenceA (x :: [Ap Fork Int])) 
    (fmap reverse (sequenceA (reverse x)))) 

उत्पादन लगता है:

*Main Text.Show.Functions> succeedingExample    
+++ OK, passed 100 tests.         
*Main Text.Show.Functions> failingExample     
*** Failed! Falsifiable (after 3 tests and 2 shrinks): 
<function>            
[0,1]    
*Main Text.Show.Functions> betterSucceedingExample 
+++ OK, passed 100 tests. 
*Main Text.Show.Functions> betterFailingExample 
*** Failed! Falsifiable (after 10 tests and 1 shrink): 
[<Ap>,<Ap>]          
+0

आह, दिलचस्प। क्या 'फोर्क' यहां 'कहीं भी' पर कुछ भी ऑफर करता है? – dfeuer

+0

इसमें 'या तो' से भिन्न प्रकार है। उस ने कहा, आप 'फोर्क' के बजाय स्पष्ट (?) मनमाने ढंग से उदाहरण के साथ 'लेबल लेबल ए = लेबल किए गए स्ट्रिंग ए' को डेटा कर सकते हैं। तकनीकी रूप से, मुझे विश्वास नहीं है कि यह भेदभाव शक्ति को बढ़ाएगा लेकिन यह क्विक चेक के लिए काउंटर-उदाहरणों को आसान बना सकता है (हालांकि मुझे लगता है कि केवल दुर्लभ रूप से)। –

+0

आह, मैं उस बदसूरत भेद को याद किया। – dfeuer

संबंधित मुद्दे