Не удовлетворен другим моим ответом, я придумал потрясающий.
-- arb.hs
import Test.QuickCheck
import Control.Monad (liftM)
data SimpleType = SimpleType Int Char Bool String deriving(Show, Eq)
uncurry4 f (a,b,c,d) = f a b c d
instance Arbitrary SimpleType where
arbitrary = uncurry4 SimpleType `liftM` arbitrary
-- ^ this line is teh pwnzors.
-- Note how easily it can be adapted to other "simple" data types
ghci> :l arb.hs
[1 of 1] Compiling Main ( arb.hs, interpreted )
Ok, modules loaded: Main.
ghci> sample (arbitrary :: Gen SimpleType)
>>>a bunch of "Loading package" statements<<<
SimpleType 1 'B' False ""
SimpleType 0 '\n' True ""
SimpleType 0 '\186' False "\208! \227"
...
Длительное объяснение того, как я понял это
Так вот как я понял.Мне было интересно, "ну как же уже существует экземпляр Arbitrary
для (Int, Int, Int, Int)
? Я уверен, что никто не написал его, так что он должен быть как-то получен. Конечно, я нашел следующее в документы для экземпляров Arbitrary :
(Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d) => Arbitrary (a, b, c, d)
Хорошо, если они уже определили это, то почему бы не злоупотреблять им? Простые типы, которые просто составлены из меньших произвольных типов данных, не являютсясильно отличается от просто кортежа.
Так что теперь мне нужно каким-то образом преобразовать «произвольный» метод для 4-кортежа, чтобы он работал для моего типа. Вероятно, задействовано нерегулярное выполнение.
StopHoogle time!
(Мы можем легко определить наш собственный uncurry4
, поэтому предположим, что у нас уже есть это для работы.)
У меня есть генератор, arbitrary :: Gen (q,r,s,t)
(где q,r, s, t - все случаи Произвольного). Но давайте просто скажем, что это arbitrary :: Gen a
. Другими словами, a
представляет (q,r,s,t)
. У меня есть функция uncurry4
, которая имеет тип (q -> r -> s -> t -> b) -> (q,r,s,t) -> b
. Мыочевидно, что мы будем применять uncurry4 к нашему конструктору SimpleType
, поэтому uncurry4 SimpleType
имеетpe (q,r,s,t) -> SimpleType
.Давайте оставим возвращаемое значение общим, потому что Hoogle не знает о нашем SimpleType.Итак, помня наше определение a
, мы имеем по существу uncurry4 SimpleType :: a -> b
.
Итак, у меня есть Gen a
и функция a -> b
.И я хочу Gen b
результат.(Помните, что для нашей ситуации a
- это (q,r,s,t)
, а b
- это SimpleType
).Поэтому я ищу функцию с сигнатурой этого типа: Gen a -> (a -> b) -> Gen b
. Обдумав это и зная, что Gen
является экземпляром Monad
, я сразу же распознаю liftM
как монадическое магическое решение моих проблем.
Google снова спасает день,Я знал, что, возможно, был какой-то «подъёмный» комбинатор, чтобы получить желаемый результат, но я, честно говоря, не думал использовать liftM (durrr!), Пока не набрал подпись типа.