С помощью QuickCheck можно написать параметрически полиморфные свойства, например:
associativityLaw :: (Eq a, Show a, Semigroup a) => a -> a -> a -> Property
associativityLaw x y z = (x <> y) <> z === x <> (y <> z)
Это всего лишь пример, поскольку мои действительные свойства более сложны, но они достаточно хорошо иллюстрируют проблему.Это свойство проверяет, что для типа a
оператор <>
является ассоциативным.
Представьте, что я хотел бы использовать это свойство для нескольких типов.Я мог бы определить свой список тестов следующим образом:
tests =
[
testGroup "Monoid laws" [
testProperty "Associativity law, [Int]" (associativityLaw :: [Int] -> [Int] -> [Int] -> Property),
testProperty "Associativity law, Sum Int" (associativityLaw :: Sum Int -> Sum Int -> Sum Int -> Property)
]
]
Это работает, но кажется излишне многословным.Я хотел бы иметь возможность просто заявить, что для данного свойства a
должно быть [Int]
, или a
должно быть Sum Int
.
Примерно так: гипотетический синтаксис:
testProperty "Associativity law, [Int]" (associativityLaw :: a = [Int]),
testProperty "Associativity law, Sum Int" (associativityLaw :: a = Sum Int)
Есть ли способ сделать это, возможно, с расширением языка GHC?
Моя настоящая проблема связана с типами с более высоким родом, и я хотел бы иметь возможностьчтобы сказать, что, например, f a
это [Int]
, или f a
это Maybe String
.
Мне известно этот ответ , но оба варианта (Proxy
и Tagged
) кажется, как описано там, по крайней мере, слишком неловко, чтобы действительно решить проблему.