Reputation: 491
Based on the suggestion at Haskell quickBatch: Testing ZipList Monoid at mconcat results in stack overflow,
and some solutions at How can I constrain a QuickCheck parameter to a list of non-empty Strings?,
I've not been able to make this code workable:
instance Semigroup a
=> Semigroup (ZipList a) where
(<>) = liftA2 (<>)
instance Monoid a
=> Monoid (ZipList a) where
mempty = pure mempty
mappend = liftA2 mappend
mconcat as =
foldr mappend mempty as
mconcatP :: NonEmptyList a -> Property
mconcatP (nonEmptyList as) = mconcat as =-=
foldr mappend mempty as
nonEmptyList :: Gen [[Int]]
nonEmptyList = listOf1 (arbitrary :: Gen [Int])
zl :: ZipList (Sum Int)
zl = ZipList [1,1 :: Sum Int]
main :: IO ()
main = do
quickBatch $ monoid zl
I've also tried these:
import Test.QuickCheck
(NonEmptyList (..))
import Test.QuickCheck.Modifiers
(NonEmptyList (..))
ghci will throw the error:
Parse error in pattern: nonEmptyList
mconcatP (nonEmptyList as) = mconcat as =-=
I've also tried:
mconcatP (NonEmptyList as) = mconcat as =-=
The error thrown is:
Not in scope: data constructor ‘NonEmptyList’
Perhaps you meant variable ‘nonEmptyList’
I've also tried:
mconcatP :: (EqProp b, Monoid b) =>
NonEmptyList b -> Property
mconcatP (getNonEmpty -> as) =
mconcat as =-=
foldr mappend mempty as
This enabled the running of the monoid tests, but the mconcat test still hang.
I've also tried:
mconcatP :: (EqProp b, Monoid b) =>
NonEmptyList b -> Property
mconcatP (NonEmpty as) = mconcat as =-=
foldr mappend mempty as
This also enabled the running of the monoid tests, but the mconcat test still hang.
I've checked using :i NonEmptyList
, the outcome:
newtype NonEmptyList a = NonEmpty {getNonEmpty :: [a]}
-- Defined in ‘Test.QuickCheck.Modifiers’
instance Eq a => Eq (NonEmptyList a)
-- Defined in ‘Test.QuickCheck.Modifiers’
instance Functor NonEmptyList
-- Defined in ‘Test.QuickCheck.Modifiers’
instance Ord a => Ord (NonEmptyList a)
-- Defined in ‘Test.QuickCheck.Modifiers’
instance Show a => Show (NonEmptyList a)
-- Defined in ‘Test.QuickCheck.Modifiers’
instance Read a => Read (NonEmptyList a)
-- Defined in ‘Test.QuickCheck.Modifiers’
instance Arbitrary a => Arbitrary (NonEmptyList a)
-- Defined in ‘Test.QuickCheck.Modifiers’
My Cabal file is very simple:
-- testing.cabal
name: testing
version: 0.1.0.0
license-file: LICENSE
author: Max
maintainer: maxloo
build-type: Simple
cabal-version: >=1.10
library
exposed-modules: Testing
ghc-options: -Wall -fwarn-tabs
build-depends: base >=4.7 && <5
, hspec
, QuickCheck
, checkers
hs-source-dirs: .
default-language: Haskell2010
Why does it not work? How do I change mconcatP's code to enable the mconcat test from the 5 quickBatch monoid tests to run without stack overflow?
Upvotes: 0
Views: 75
Reputation: 3924
When you write
functionName pat1 = result
then GHC expects that pat1
is a pattern. Patterns are very specific things that need to either be identifier names that get bound to inputs or constructors that get matched to data type shapes (and also the extensions ViewPatterns
and PatternSynonyms
that extend this further).
So, when you write
mconcatP (nonEmptyList as) = as
then GHC becomes very confused. nonEmptyList
is not a constructor names (constructors always start uppercase), and because it's in parentheses with another identifier, it's not a lone identifier to bind input. Therefore, GHC concludes rightly that this isn't a valid pattern.
What you're looking for is:
mconcatP (NonEmpty as) = ...
There are a few alternatives you could write too! For instance, with ViewPatterns
enabled, you could write:
mconcatP (getNonEmptyList -> as) = ...
This is a "view pattern", which tells GHC to take the argument, apply the function getNonEmptyList
to it, and then bind the result to the identifier as
.
Since this is QuickCheck, you can also use forAll
, which allows you to supply a generator and a function over the generated value to produce a property. It would look like this:
mconcatP = forAll nonEmptyList $ \as -> ...
Upvotes: 1