No estoy satisfecho con mi otra respuesta, he encontrado una más impresionante.
-- 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"
...
larga explicación de cómo me di cuenta de esto
Así que aquí es como lo tengo. ?. Me preguntaba, "bien cómo es ya una instancia Arbitrary
para (Int, Int, Int, Int)
Estoy seguro de que nadie escribió ella, por lo que debe derivarse de alguna manera Efectivamente, encontré lo siguiente en el docs for instances of Arbitrary:
(Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d) => Arbitrary (a, b, c, d)
Bueno, si ya tienen que definirse, entonces por qué no abusar de ella? los tipos simples que son sólo está formada por tipos de datos arbitrarios más pequeños no son muy diferentes que una tupla.
Así que ahora tengo que transformar de alguna manera el " arbitrario "método para la 4-tupla para que funcione para mi tipo. Es probable que haya deslices.
Parada. Hoogle tiempo!
(Podemos definir fácilmente nuestra propia uncurry4
, por lo que asumir que ya tenemos esto para operar.)
Tengo un generador, arbitrary :: Gen (q,r,s,t)
(donde q, r, s, t son todas las instancias de arbitraria). Pero digamos que es arbitrary :: Gen a
. En otras palabras, a
representa (q,r,s,t)
. Tengo una función, uncurry4
, que tiene el tipo (q -> r -> s -> t -> b) -> (q,r,s,t) -> b
. Obviamente vamos a aplicar uncurry4 a nuestro SimpleType
constructor. Entonces, uncurry4 SimpleType
tiene el tipo (q,r,s,t) -> SimpleType
. Sin embargo, vamos a mantener el valor de retorno genérico, porque Hoogle no conoce nuestro SimpleType. Así que recordando nuestra definición de a
, tenemos esencialmente uncurry4 SimpleType :: a -> b
.
Así que tengo un Gen a
y una función a -> b
. Y quiero un resultado de Gen b
.(Recuerde, para nuestra situación, a
es (q,r,s,t)
y b
es SimpleType
). Así que estoy buscando una función con esta firma de tipo: Gen a -> (a -> b) -> Gen b
. Hoogling that, y sabiendo que Gen
es una instancia de Monad
, reconozco inmediatamente liftM
como la solución mágico-monádica a mis problemas.
Hoogle vuelve a salvar el día. Sabía que probablemente había algún combinador de "elevación" para obtener el resultado deseado, pero honestamente no pensé usar LiftM (durrr!) Hasta que encontré la firma de tipo.
gran 'convert' hack! Creo que puede deshacerse de OverlappingInstances restringiendo el número de aplicaciones 'arbitrarias' con el número de nivel de tipo (como yo lo hice). –