Как написать экземпляр `Semigroup` и их`quickCheck` для параметризованных типов?
В упражнениях по программированию на Haskell из Первой книги принципов по полугруппе меня просят написать quickCheck
для пользовательских классов типов. Классов типов много, но я не понимаю, как писать даже базовые:
Проблемы:
Первый для 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
Второй для
newtype Identity a = Identity a
и третий для:
data Two a b =
Two a b
Мои ответы:
Во-первых, я изменил instance
выражение для
instance Semigroup Trivial where
_ <> _ = Trivial
и это работает.
Я попробовал следующий код, но не работает для второго:
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)
Я не понимаю, что quickTest
следует проверить здесь. Я даже попробовал:
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)
сделать параметры параметризованных типов конкретными. Но это тоже не работает.
Для третьего я не знаю, как их написать. Но я думаю, что это похоже на второй.
Может кто-нибудь объяснить на них, чтобы я мог понять, как написать instance
параметризованных полугрупп и их quickTest
произвольно?
1 ответ
Это не верно:
instance Arbitrary (Identity a) where
arbitrary = return (Identity a)
a
это не переменная значения, это переменная типа. Нам нужно значение типа a
перейти к Identity
конструктор, а не a
набери сам.
Итак, нам нужно что-то вроде
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)
(или, более кратко, arbitrary = Identity <$> arbitrary
)
Обратите внимание, как мы должны требовать, чтобы a
это тип, для которого мы можем генерировать случайные выборки (добавление Arbitrary a =>
после Instance
). В противном случае мы не можем использовать x <- arbitrary
создать образец для a
,
В дальнейшем:
type IdentityAssoc =
(Identity a0) -> (Identity a1) -> (Identity a2) -> Bool
Здесь мы не можем сослаться на a1,a1,a2
, поскольку мы нигде не определили эти типы. Нам нужно выбрать конкретные типы, такие как Int
, Кроме того, эти три типа должны быть одного типа, так как (<>)
принимает два значения одного типа и возвращает значение этого типа.