Не удовлетворенным моим другим ответом, я придумал удивительный вариант.
-- 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)
Я уверен, что никто не писал, поэтому оно должно быть получено каким-то образом Конечно же, я нашел следующее в docs for instances of Arbitrary:
(Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d) => Arbitrary (a, b, c, d)
Ну, если у них уже есть, что определено, то почему бы не злоупотреблять? Простые типы, которые просто состоят из небольших произвольных типов данных, которые не сильно отличаются от всего кортежа.
Так что теперь мне нужно как-то преобразование " произвольный "метод для 4-кортежей, так что он работает для моего типа. Возможно, будет задействован немедленный запуск.
Остановить. Время в Hoogle!
(Мы можем легко определить наши собственные uncurry4
, поэтому предположим, что мы уже имеем это работать с.)
У меня есть генератор, arbitrary :: Gen (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
имеет тип (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
. Hoogling that, и зная, что Gen
является примером Monad
, я сразу же признаю liftM
в качестве монадически-магического решения моих проблем.
Hoogle сохраняет этот день снова. Я знал, что, вероятно, есть какой-то «подъемный» комбинатор, чтобы получить желаемый результат, но я, честно говоря, не думал использовать liftM (durrr!) До тех пор, пока не подхватил подпись типа.
отличный 'конвертировать' взломать! Я думаю, вы можете избавиться от OverlappingInstances, сместив количество «произвольных» приложений с номером типа (как и я). –