Как написать экземпляр `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, Кроме того, эти три типа должны быть одного типа, так как (<>) принимает два значения одного типа и возвращает значение этого типа.

Другие вопросы по тегам