简体   繁体   中英

How to write `Semigroup` instance and their `quickCheck`s on parameterized types?

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:

Problems:

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

My answers:

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM