QuickCheck properties for function that sorts a list of strings

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
7
down vote

favorite












I'm reading The Haskell Road to Logic, Math and Programming and for each exercise solution I write some properties to learn QuickCheck. One of the exercises is to write a function that sorts a list of strings (srtStr). I wonder if my properties are right. Also, is there something missing?



propIdempotent ∷ [String] → Bool
propIdempotent xs = srtStr xs == srtStr (srtStr xs)

propLength ∷ [String] → Bool
propLength str = length str == length (srtStr str)

propOrder ∷ [String] → Bool
propOrder xs = process (srtStr xs) True where
process ∷ [String] → Bool → Bool
process (x : y : xs) state = process xs (if state then x <= y else False)
process _ state = state

check ∷ IO ()
check = do
quickCheck propIdempotent
quickCheck propLength
quickCheck propOrder






share|improve this question















  • 1




    Your process function should short-circuit. if you prefer you don't need to evaluateprocess xs if state is false. There are different way to achieve this.
    – mb14
    Apr 5 at 20:54
















up vote
7
down vote

favorite












I'm reading The Haskell Road to Logic, Math and Programming and for each exercise solution I write some properties to learn QuickCheck. One of the exercises is to write a function that sorts a list of strings (srtStr). I wonder if my properties are right. Also, is there something missing?



propIdempotent ∷ [String] → Bool
propIdempotent xs = srtStr xs == srtStr (srtStr xs)

propLength ∷ [String] → Bool
propLength str = length str == length (srtStr str)

propOrder ∷ [String] → Bool
propOrder xs = process (srtStr xs) True where
process ∷ [String] → Bool → Bool
process (x : y : xs) state = process xs (if state then x <= y else False)
process _ state = state

check ∷ IO ()
check = do
quickCheck propIdempotent
quickCheck propLength
quickCheck propOrder






share|improve this question















  • 1




    Your process function should short-circuit. if you prefer you don't need to evaluateprocess xs if state is false. There are different way to achieve this.
    – mb14
    Apr 5 at 20:54












up vote
7
down vote

favorite









up vote
7
down vote

favorite











I'm reading The Haskell Road to Logic, Math and Programming and for each exercise solution I write some properties to learn QuickCheck. One of the exercises is to write a function that sorts a list of strings (srtStr). I wonder if my properties are right. Also, is there something missing?



propIdempotent ∷ [String] → Bool
propIdempotent xs = srtStr xs == srtStr (srtStr xs)

propLength ∷ [String] → Bool
propLength str = length str == length (srtStr str)

propOrder ∷ [String] → Bool
propOrder xs = process (srtStr xs) True where
process ∷ [String] → Bool → Bool
process (x : y : xs) state = process xs (if state then x <= y else False)
process _ state = state

check ∷ IO ()
check = do
quickCheck propIdempotent
quickCheck propLength
quickCheck propOrder






share|improve this question











I'm reading The Haskell Road to Logic, Math and Programming and for each exercise solution I write some properties to learn QuickCheck. One of the exercises is to write a function that sorts a list of strings (srtStr). I wonder if my properties are right. Also, is there something missing?



propIdempotent ∷ [String] → Bool
propIdempotent xs = srtStr xs == srtStr (srtStr xs)

propLength ∷ [String] → Bool
propLength str = length str == length (srtStr str)

propOrder ∷ [String] → Bool
propOrder xs = process (srtStr xs) True where
process ∷ [String] → Bool → Bool
process (x : y : xs) state = process xs (if state then x <= y else False)
process _ state = state

check ∷ IO ()
check = do
quickCheck propIdempotent
quickCheck propLength
quickCheck propOrder








share|improve this question










share|improve this question




share|improve this question









asked Apr 5 at 20:20









Tae

1604




1604







  • 1




    Your process function should short-circuit. if you prefer you don't need to evaluateprocess xs if state is false. There are different way to achieve this.
    – mb14
    Apr 5 at 20:54












  • 1




    Your process function should short-circuit. if you prefer you don't need to evaluateprocess xs if state is false. There are different way to achieve this.
    – mb14
    Apr 5 at 20:54







1




1




Your process function should short-circuit. if you prefer you don't need to evaluateprocess xs if state is false. There are different way to achieve this.
– mb14
Apr 5 at 20:54




Your process function should short-circuit. if you prefer you don't need to evaluateprocess xs if state is false. There are different way to achieve this.
– mb14
Apr 5 at 20:54










3 Answers
3






active

oldest

votes

















up vote
4
down vote



accepted










Correctness of a sorting algorithm consists of showing that the resulting list is in order which your propOrder takes care of and that the resulting list is a permutation of the other list. Your propLength does the latter partially. It prevents you from adding or removing an element out of thin air but doesn't prevent you to replace one with another.



You can write a permutation checker as follows:



First define a subbag relation that is every element of the first list appears in the second.



isSubBag :: Eq a => [ a ] -> [ a ] -> Bool
isSubBag ys = True
isSubBag (x:xs) ys = x `elem` ys && isSubBag xs (delete x ys)


Then you can write a permutation checker by applying it both ways.



isPerm :: Eq a => [ a ] -> [ a ] -> Bool
isPerm xs ys = isSubBag xs ys && isSubBag ys xs


Your property comes out as:



propPerm :: [ String ] -> Bool
propPerm xs = isPerm xs (srtStr xs)


Overall, you can improve your process function by eliminating accumulator and using the built-in conjunction. Also a better name for it would be isInOrder



process :: [String] -> Bool
process (x : y : xs) = x <= y && process (y : xs)
process _ = True





share|improve this answer



















  • 1




    You can now directly check for bag equality by replacing that True with null ys.
    – Gurkenglas
    Apr 5 at 21:38

















up vote
2
down vote













One property that comes to mind is that applying any permutation before srtStr does not change the result. But what is a permutation? I'd say it's a function that doesn't exploit properties of the element type, has a left inverse and preserves the length. (The first ensures that only input elements are used. The second ensures that no elements can be dropped. The third ensures that there is no space for duplicated elements.)



propPermutationInvariant
:: (forall a. [a] -> [a]) -> (forall a. [a] -> [a]) -> [String] -> Bool
propPermutationInvariant permutation inverse xs
= length (permutation xs) /= length xs
|| inverse (permutation xs) /= xs
|| srtStr xs == srtStr (permutation xs)


For example, this implies srtStr xs == strStr (reverse xs), via propPermutationInvariant reverse reverse xs.






share|improve this answer























  • Checking that applying a permutation before doesn't change the result isn't strong enough. For example, if the output is a singleton with the minimal element, it satisfies this property. Or a list with replicated this minimal element. But it'd be sufficient if combined with another property - that the sorting function in question is an identity on a sequence that's already sorted.
    – Petr Pudlák
    Apr 6 at 16:07










  • It's just a necessary property, not a sufficient one. The function that sorts into reverse order also satisfies this.
    – Gurkenglas
    Apr 7 at 14:04

















up vote
1
down vote













In addition to the other great answers, I'd like to point out a few details.



First, in your process function you skip too far, you need to go through elements one by one. Your code applied to [4, 5, 2, 3, 0, 1] passes. Rather you need to pattern match like this:



... process (x : xs@(y : _)) state = process xs (if state then x <= y else False)


Next, as pointed out in the comment, it's better to short-circuit if the property fails. It's more efficient and also more idiomatic in Haskell. Use an accumulator only if you need it. Something like this:



prop_order :: [String] -> Bool
prop_order (x : xs@(y : _)) = (x <= y) && prop_order xs
prop_order _ = property ()


Also note that (if state then x <= y else False) can be simplified to state && (x <= y).



Finally, I'd suggest you to express your property in such a way that you get more detailed information about what's wrong in a failing test. This can be accomplished using Property instead of Bool as the result type and using combinators in Test.QuickCheck.Property:



import Test.QuickCheck
import Test.QuickCheck.Property

-- | Tests if left is less or equal than right.
(<?=) :: (Ord a, Show a) => a -> a -> Property
x <?= y = counterexample (show x ++ " must be less than " ++ show y) (x <= y)

prop_order :: [String] -> Property
prop_order (x : xs@(y : _)) = (x <?= y) .&&. prop_order xs
prop_order _ = property ()


Note that operators (.&.) and (.&&.) are very different!



I'd probably prefer yet another variant using conjoin, which spares you of the explicit recursion. By zipping a list with its tail we get all consecutive pairs, and then we just express that each of such pairs must satisfy <?=.



prop_order2 :: [String] -> Property
prop_order2 xs = conjoin $ zipWith (<?=) xs (tail xs)





share|improve this answer





















    Your Answer




    StackExchange.ifUsing("editor", function ()
    return StackExchange.using("mathjaxEditing", function ()
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    );
    );
    , "mathjax-editing");

    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "196"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );








     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191353%2fquickcheck-properties-for-function-that-sorts-a-list-of-strings%23new-answer', 'question_page');

    );

    Post as a guest






























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    4
    down vote



    accepted










    Correctness of a sorting algorithm consists of showing that the resulting list is in order which your propOrder takes care of and that the resulting list is a permutation of the other list. Your propLength does the latter partially. It prevents you from adding or removing an element out of thin air but doesn't prevent you to replace one with another.



    You can write a permutation checker as follows:



    First define a subbag relation that is every element of the first list appears in the second.



    isSubBag :: Eq a => [ a ] -> [ a ] -> Bool
    isSubBag ys = True
    isSubBag (x:xs) ys = x `elem` ys && isSubBag xs (delete x ys)


    Then you can write a permutation checker by applying it both ways.



    isPerm :: Eq a => [ a ] -> [ a ] -> Bool
    isPerm xs ys = isSubBag xs ys && isSubBag ys xs


    Your property comes out as:



    propPerm :: [ String ] -> Bool
    propPerm xs = isPerm xs (srtStr xs)


    Overall, you can improve your process function by eliminating accumulator and using the built-in conjunction. Also a better name for it would be isInOrder



    process :: [String] -> Bool
    process (x : y : xs) = x <= y && process (y : xs)
    process _ = True





    share|improve this answer



















    • 1




      You can now directly check for bag equality by replacing that True with null ys.
      – Gurkenglas
      Apr 5 at 21:38














    up vote
    4
    down vote



    accepted










    Correctness of a sorting algorithm consists of showing that the resulting list is in order which your propOrder takes care of and that the resulting list is a permutation of the other list. Your propLength does the latter partially. It prevents you from adding or removing an element out of thin air but doesn't prevent you to replace one with another.



    You can write a permutation checker as follows:



    First define a subbag relation that is every element of the first list appears in the second.



    isSubBag :: Eq a => [ a ] -> [ a ] -> Bool
    isSubBag ys = True
    isSubBag (x:xs) ys = x `elem` ys && isSubBag xs (delete x ys)


    Then you can write a permutation checker by applying it both ways.



    isPerm :: Eq a => [ a ] -> [ a ] -> Bool
    isPerm xs ys = isSubBag xs ys && isSubBag ys xs


    Your property comes out as:



    propPerm :: [ String ] -> Bool
    propPerm xs = isPerm xs (srtStr xs)


    Overall, you can improve your process function by eliminating accumulator and using the built-in conjunction. Also a better name for it would be isInOrder



    process :: [String] -> Bool
    process (x : y : xs) = x <= y && process (y : xs)
    process _ = True





    share|improve this answer



















    • 1




      You can now directly check for bag equality by replacing that True with null ys.
      – Gurkenglas
      Apr 5 at 21:38












    up vote
    4
    down vote



    accepted







    up vote
    4
    down vote



    accepted






    Correctness of a sorting algorithm consists of showing that the resulting list is in order which your propOrder takes care of and that the resulting list is a permutation of the other list. Your propLength does the latter partially. It prevents you from adding or removing an element out of thin air but doesn't prevent you to replace one with another.



    You can write a permutation checker as follows:



    First define a subbag relation that is every element of the first list appears in the second.



    isSubBag :: Eq a => [ a ] -> [ a ] -> Bool
    isSubBag ys = True
    isSubBag (x:xs) ys = x `elem` ys && isSubBag xs (delete x ys)


    Then you can write a permutation checker by applying it both ways.



    isPerm :: Eq a => [ a ] -> [ a ] -> Bool
    isPerm xs ys = isSubBag xs ys && isSubBag ys xs


    Your property comes out as:



    propPerm :: [ String ] -> Bool
    propPerm xs = isPerm xs (srtStr xs)


    Overall, you can improve your process function by eliminating accumulator and using the built-in conjunction. Also a better name for it would be isInOrder



    process :: [String] -> Bool
    process (x : y : xs) = x <= y && process (y : xs)
    process _ = True





    share|improve this answer















    Correctness of a sorting algorithm consists of showing that the resulting list is in order which your propOrder takes care of and that the resulting list is a permutation of the other list. Your propLength does the latter partially. It prevents you from adding or removing an element out of thin air but doesn't prevent you to replace one with another.



    You can write a permutation checker as follows:



    First define a subbag relation that is every element of the first list appears in the second.



    isSubBag :: Eq a => [ a ] -> [ a ] -> Bool
    isSubBag ys = True
    isSubBag (x:xs) ys = x `elem` ys && isSubBag xs (delete x ys)


    Then you can write a permutation checker by applying it both ways.



    isPerm :: Eq a => [ a ] -> [ a ] -> Bool
    isPerm xs ys = isSubBag xs ys && isSubBag ys xs


    Your property comes out as:



    propPerm :: [ String ] -> Bool
    propPerm xs = isPerm xs (srtStr xs)


    Overall, you can improve your process function by eliminating accumulator and using the built-in conjunction. Also a better name for it would be isInOrder



    process :: [String] -> Bool
    process (x : y : xs) = x <= y && process (y : xs)
    process _ = True






    share|improve this answer















    share|improve this answer



    share|improve this answer








    edited Apr 5 at 21:30


























    answered Apr 5 at 20:52









    Madgen

    562




    562







    • 1




      You can now directly check for bag equality by replacing that True with null ys.
      – Gurkenglas
      Apr 5 at 21:38












    • 1




      You can now directly check for bag equality by replacing that True with null ys.
      – Gurkenglas
      Apr 5 at 21:38







    1




    1




    You can now directly check for bag equality by replacing that True with null ys.
    – Gurkenglas
    Apr 5 at 21:38




    You can now directly check for bag equality by replacing that True with null ys.
    – Gurkenglas
    Apr 5 at 21:38












    up vote
    2
    down vote













    One property that comes to mind is that applying any permutation before srtStr does not change the result. But what is a permutation? I'd say it's a function that doesn't exploit properties of the element type, has a left inverse and preserves the length. (The first ensures that only input elements are used. The second ensures that no elements can be dropped. The third ensures that there is no space for duplicated elements.)



    propPermutationInvariant
    :: (forall a. [a] -> [a]) -> (forall a. [a] -> [a]) -> [String] -> Bool
    propPermutationInvariant permutation inverse xs
    = length (permutation xs) /= length xs
    || inverse (permutation xs) /= xs
    || srtStr xs == srtStr (permutation xs)


    For example, this implies srtStr xs == strStr (reverse xs), via propPermutationInvariant reverse reverse xs.






    share|improve this answer























    • Checking that applying a permutation before doesn't change the result isn't strong enough. For example, if the output is a singleton with the minimal element, it satisfies this property. Or a list with replicated this minimal element. But it'd be sufficient if combined with another property - that the sorting function in question is an identity on a sequence that's already sorted.
      – Petr Pudlák
      Apr 6 at 16:07










    • It's just a necessary property, not a sufficient one. The function that sorts into reverse order also satisfies this.
      – Gurkenglas
      Apr 7 at 14:04














    up vote
    2
    down vote













    One property that comes to mind is that applying any permutation before srtStr does not change the result. But what is a permutation? I'd say it's a function that doesn't exploit properties of the element type, has a left inverse and preserves the length. (The first ensures that only input elements are used. The second ensures that no elements can be dropped. The third ensures that there is no space for duplicated elements.)



    propPermutationInvariant
    :: (forall a. [a] -> [a]) -> (forall a. [a] -> [a]) -> [String] -> Bool
    propPermutationInvariant permutation inverse xs
    = length (permutation xs) /= length xs
    || inverse (permutation xs) /= xs
    || srtStr xs == srtStr (permutation xs)


    For example, this implies srtStr xs == strStr (reverse xs), via propPermutationInvariant reverse reverse xs.






    share|improve this answer























    • Checking that applying a permutation before doesn't change the result isn't strong enough. For example, if the output is a singleton with the minimal element, it satisfies this property. Or a list with replicated this minimal element. But it'd be sufficient if combined with another property - that the sorting function in question is an identity on a sequence that's already sorted.
      – Petr Pudlák
      Apr 6 at 16:07










    • It's just a necessary property, not a sufficient one. The function that sorts into reverse order also satisfies this.
      – Gurkenglas
      Apr 7 at 14:04












    up vote
    2
    down vote










    up vote
    2
    down vote









    One property that comes to mind is that applying any permutation before srtStr does not change the result. But what is a permutation? I'd say it's a function that doesn't exploit properties of the element type, has a left inverse and preserves the length. (The first ensures that only input elements are used. The second ensures that no elements can be dropped. The third ensures that there is no space for duplicated elements.)



    propPermutationInvariant
    :: (forall a. [a] -> [a]) -> (forall a. [a] -> [a]) -> [String] -> Bool
    propPermutationInvariant permutation inverse xs
    = length (permutation xs) /= length xs
    || inverse (permutation xs) /= xs
    || srtStr xs == srtStr (permutation xs)


    For example, this implies srtStr xs == strStr (reverse xs), via propPermutationInvariant reverse reverse xs.






    share|improve this answer















    One property that comes to mind is that applying any permutation before srtStr does not change the result. But what is a permutation? I'd say it's a function that doesn't exploit properties of the element type, has a left inverse and preserves the length. (The first ensures that only input elements are used. The second ensures that no elements can be dropped. The third ensures that there is no space for duplicated elements.)



    propPermutationInvariant
    :: (forall a. [a] -> [a]) -> (forall a. [a] -> [a]) -> [String] -> Bool
    propPermutationInvariant permutation inverse xs
    = length (permutation xs) /= length xs
    || inverse (permutation xs) /= xs
    || srtStr xs == srtStr (permutation xs)


    For example, this implies srtStr xs == strStr (reverse xs), via propPermutationInvariant reverse reverse xs.







    share|improve this answer















    share|improve this answer



    share|improve this answer








    edited Apr 5 at 21:08


























    answered Apr 5 at 21:01









    Gurkenglas

    2,658411




    2,658411











    • Checking that applying a permutation before doesn't change the result isn't strong enough. For example, if the output is a singleton with the minimal element, it satisfies this property. Or a list with replicated this minimal element. But it'd be sufficient if combined with another property - that the sorting function in question is an identity on a sequence that's already sorted.
      – Petr Pudlák
      Apr 6 at 16:07










    • It's just a necessary property, not a sufficient one. The function that sorts into reverse order also satisfies this.
      – Gurkenglas
      Apr 7 at 14:04
















    • Checking that applying a permutation before doesn't change the result isn't strong enough. For example, if the output is a singleton with the minimal element, it satisfies this property. Or a list with replicated this minimal element. But it'd be sufficient if combined with another property - that the sorting function in question is an identity on a sequence that's already sorted.
      – Petr Pudlák
      Apr 6 at 16:07










    • It's just a necessary property, not a sufficient one. The function that sorts into reverse order also satisfies this.
      – Gurkenglas
      Apr 7 at 14:04















    Checking that applying a permutation before doesn't change the result isn't strong enough. For example, if the output is a singleton with the minimal element, it satisfies this property. Or a list with replicated this minimal element. But it'd be sufficient if combined with another property - that the sorting function in question is an identity on a sequence that's already sorted.
    – Petr Pudlák
    Apr 6 at 16:07




    Checking that applying a permutation before doesn't change the result isn't strong enough. For example, if the output is a singleton with the minimal element, it satisfies this property. Or a list with replicated this minimal element. But it'd be sufficient if combined with another property - that the sorting function in question is an identity on a sequence that's already sorted.
    – Petr Pudlák
    Apr 6 at 16:07












    It's just a necessary property, not a sufficient one. The function that sorts into reverse order also satisfies this.
    – Gurkenglas
    Apr 7 at 14:04




    It's just a necessary property, not a sufficient one. The function that sorts into reverse order also satisfies this.
    – Gurkenglas
    Apr 7 at 14:04










    up vote
    1
    down vote













    In addition to the other great answers, I'd like to point out a few details.



    First, in your process function you skip too far, you need to go through elements one by one. Your code applied to [4, 5, 2, 3, 0, 1] passes. Rather you need to pattern match like this:



    ... process (x : xs@(y : _)) state = process xs (if state then x <= y else False)


    Next, as pointed out in the comment, it's better to short-circuit if the property fails. It's more efficient and also more idiomatic in Haskell. Use an accumulator only if you need it. Something like this:



    prop_order :: [String] -> Bool
    prop_order (x : xs@(y : _)) = (x <= y) && prop_order xs
    prop_order _ = property ()


    Also note that (if state then x <= y else False) can be simplified to state && (x <= y).



    Finally, I'd suggest you to express your property in such a way that you get more detailed information about what's wrong in a failing test. This can be accomplished using Property instead of Bool as the result type and using combinators in Test.QuickCheck.Property:



    import Test.QuickCheck
    import Test.QuickCheck.Property

    -- | Tests if left is less or equal than right.
    (<?=) :: (Ord a, Show a) => a -> a -> Property
    x <?= y = counterexample (show x ++ " must be less than " ++ show y) (x <= y)

    prop_order :: [String] -> Property
    prop_order (x : xs@(y : _)) = (x <?= y) .&&. prop_order xs
    prop_order _ = property ()


    Note that operators (.&.) and (.&&.) are very different!



    I'd probably prefer yet another variant using conjoin, which spares you of the explicit recursion. By zipping a list with its tail we get all consecutive pairs, and then we just express that each of such pairs must satisfy <?=.



    prop_order2 :: [String] -> Property
    prop_order2 xs = conjoin $ zipWith (<?=) xs (tail xs)





    share|improve this answer

























      up vote
      1
      down vote













      In addition to the other great answers, I'd like to point out a few details.



      First, in your process function you skip too far, you need to go through elements one by one. Your code applied to [4, 5, 2, 3, 0, 1] passes. Rather you need to pattern match like this:



      ... process (x : xs@(y : _)) state = process xs (if state then x <= y else False)


      Next, as pointed out in the comment, it's better to short-circuit if the property fails. It's more efficient and also more idiomatic in Haskell. Use an accumulator only if you need it. Something like this:



      prop_order :: [String] -> Bool
      prop_order (x : xs@(y : _)) = (x <= y) && prop_order xs
      prop_order _ = property ()


      Also note that (if state then x <= y else False) can be simplified to state && (x <= y).



      Finally, I'd suggest you to express your property in such a way that you get more detailed information about what's wrong in a failing test. This can be accomplished using Property instead of Bool as the result type and using combinators in Test.QuickCheck.Property:



      import Test.QuickCheck
      import Test.QuickCheck.Property

      -- | Tests if left is less or equal than right.
      (<?=) :: (Ord a, Show a) => a -> a -> Property
      x <?= y = counterexample (show x ++ " must be less than " ++ show y) (x <= y)

      prop_order :: [String] -> Property
      prop_order (x : xs@(y : _)) = (x <?= y) .&&. prop_order xs
      prop_order _ = property ()


      Note that operators (.&.) and (.&&.) are very different!



      I'd probably prefer yet another variant using conjoin, which spares you of the explicit recursion. By zipping a list with its tail we get all consecutive pairs, and then we just express that each of such pairs must satisfy <?=.



      prop_order2 :: [String] -> Property
      prop_order2 xs = conjoin $ zipWith (<?=) xs (tail xs)





      share|improve this answer























        up vote
        1
        down vote










        up vote
        1
        down vote









        In addition to the other great answers, I'd like to point out a few details.



        First, in your process function you skip too far, you need to go through elements one by one. Your code applied to [4, 5, 2, 3, 0, 1] passes. Rather you need to pattern match like this:



        ... process (x : xs@(y : _)) state = process xs (if state then x <= y else False)


        Next, as pointed out in the comment, it's better to short-circuit if the property fails. It's more efficient and also more idiomatic in Haskell. Use an accumulator only if you need it. Something like this:



        prop_order :: [String] -> Bool
        prop_order (x : xs@(y : _)) = (x <= y) && prop_order xs
        prop_order _ = property ()


        Also note that (if state then x <= y else False) can be simplified to state && (x <= y).



        Finally, I'd suggest you to express your property in such a way that you get more detailed information about what's wrong in a failing test. This can be accomplished using Property instead of Bool as the result type and using combinators in Test.QuickCheck.Property:



        import Test.QuickCheck
        import Test.QuickCheck.Property

        -- | Tests if left is less or equal than right.
        (<?=) :: (Ord a, Show a) => a -> a -> Property
        x <?= y = counterexample (show x ++ " must be less than " ++ show y) (x <= y)

        prop_order :: [String] -> Property
        prop_order (x : xs@(y : _)) = (x <?= y) .&&. prop_order xs
        prop_order _ = property ()


        Note that operators (.&.) and (.&&.) are very different!



        I'd probably prefer yet another variant using conjoin, which spares you of the explicit recursion. By zipping a list with its tail we get all consecutive pairs, and then we just express that each of such pairs must satisfy <?=.



        prop_order2 :: [String] -> Property
        prop_order2 xs = conjoin $ zipWith (<?=) xs (tail xs)





        share|improve this answer













        In addition to the other great answers, I'd like to point out a few details.



        First, in your process function you skip too far, you need to go through elements one by one. Your code applied to [4, 5, 2, 3, 0, 1] passes. Rather you need to pattern match like this:



        ... process (x : xs@(y : _)) state = process xs (if state then x <= y else False)


        Next, as pointed out in the comment, it's better to short-circuit if the property fails. It's more efficient and also more idiomatic in Haskell. Use an accumulator only if you need it. Something like this:



        prop_order :: [String] -> Bool
        prop_order (x : xs@(y : _)) = (x <= y) && prop_order xs
        prop_order _ = property ()


        Also note that (if state then x <= y else False) can be simplified to state && (x <= y).



        Finally, I'd suggest you to express your property in such a way that you get more detailed information about what's wrong in a failing test. This can be accomplished using Property instead of Bool as the result type and using combinators in Test.QuickCheck.Property:



        import Test.QuickCheck
        import Test.QuickCheck.Property

        -- | Tests if left is less or equal than right.
        (<?=) :: (Ord a, Show a) => a -> a -> Property
        x <?= y = counterexample (show x ++ " must be less than " ++ show y) (x <= y)

        prop_order :: [String] -> Property
        prop_order (x : xs@(y : _)) = (x <?= y) .&&. prop_order xs
        prop_order _ = property ()


        Note that operators (.&.) and (.&&.) are very different!



        I'd probably prefer yet another variant using conjoin, which spares you of the explicit recursion. By zipping a list with its tail we get all consecutive pairs, and then we just express that each of such pairs must satisfy <?=.



        prop_order2 :: [String] -> Property
        prop_order2 xs = conjoin $ zipWith (<?=) xs (tail xs)






        share|improve this answer













        share|improve this answer



        share|improve this answer











        answered Apr 8 at 9:48









        Petr Pudlák

        2,755931




        2,755931






















             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191353%2fquickcheck-properties-for-function-that-sorts-a-list-of-strings%23new-answer', 'question_page');

            );

            Post as a guest













































































            Popular posts from this blog

            Chat program with C++ and SFML

            Function to Return a JSON Like Objects Using VBA Collections and Arrays

            Will my employers contract hold up in court?