In the exercises of Haskell Programming from First Principle book on Semigroup, I am asked to write quickCheck
for user defined typeclasses. There are many typeclasses, but I do not understand how to write even the basic ones:
The first is for Trivial
:
module Exercise where
import Test.QuickCheck
data Trivial =
Trivial
deriving (Eq, Show)
instance Semigroup Trivial where
_ <> _ = undefined
instance Arbitrary Trivial where
arbitrary = return Trivial
semigroupAssoc :: (Eq m, Semigroup m) => m -> m -> m -> Bool
semigroupAssoc a b c = (a <> (b <> c)) == ((a <> b) <> c)
type TrivialAssoc = Trivial -> Trivial -> Trivial -> Bool
The second is for
newtype Identity a = Identity a
and the third is for:
data Two a b =
Two a b
For the first, I changed the instance
expression to
instance Semigroup Trivial where
_ <> _ = Trivial
and it works.
I tried the following code but not work for the second:
newtype Identity a = Identity a
instance (Semigroup a) => Semigroup (Identity a) where
(Identity a1) <> (Identity a2) = Identity (a1 <> a2)
instance Arbitrary (Identity a) where
arbitrary = return (Identity a)
type IdentityAssoc =
(Identity a0) -> (Identity a1) -> (Identity a2) -> Bool
main :: IO ()
main =
quickCheck (semigroupAssoc :: IdentityAssoc)
I find I do not understand what the quickTest
should check here. I even tried:
import Data.NonEmpty
newtype Identity a = Identity a
instance (Semigroup a) => Semigroup (Identity a) where
(Identity a1) <> (Identity a2) = Identity (a1 <> a2)
instance Arbitrary (Identity a) where
arbitrary = return (Identity a)
type IdentityAssoc =
(Identity (NonEmpty Int)) -> (Identity (NonEmpty Int)) -> (Identity (NonEmpty Int)) -> Bool
main :: IO ()
main =
quickCheck (semigroupAssoc :: IdentityAssoc)
to make the parameterized types' parameters concrete. But it does not work either.
For the third, I do not know how to write them. But I think it is similar to the second one.
Can someone explain on these so that I can understand how to write the instance
of parameterized Semigroups and their quickTest
arbitrary?
This is wrong:
instance Arbitrary (Identity a) where
arbitrary = return (Identity a)
a
is not a value variable, it is a type variable. We need a value of type a
to pass to the Identity
constructor, not the a
type itself.
So we need something like
instance Arbitrary a => Arbitrary (Identity a) where
arbitrary = do
x <- arbitrary -- generate a value of type a
return (Identity x) -- turn it into a value of type (Identity a)
(or, more concisely, arbitrary = Identity <$> arbitrary
)
Note how we have to require that a
is a type for which we can generate random samples (adding Arbitrary a =>
after Instance
). Otherwise, we can't use x <- arbitrary
to generate a sample for a
.
Further:
type IdentityAssoc =
(Identity a0) -> (Identity a1) -> (Identity a2) -> Bool
Here we can't refer to a1,a1,a2
, since we haven't defined those types anywhere. We need to choose concrete types, like Int
. Further, these three types must be the same type, since (<>)
takes two values of the same type, and returns a value in that type.