私の他の答えに満足していないので、もっと素晴らしいものを思いつきました.
-- 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 a, Arbitrary b, Arbitrary c, Arbitrary d) => Arbitrary (a, b, c, d)
ええと、彼らがすでにそれを定義しているのなら、それを悪用してみませんか? より小さな Arbitrary データ型で構成されているだけの単純な型は、単なるタプルと大差ありません。
したがって、4タプルの「任意の」メソッドを何らかの方法で変換して、自分のタイプで機能するようにする必要があります。Uncurring が関与している可能性があります。
止まる。ホーグルタイム!
(独自の を簡単に定義できるuncurry4ので、これを操作するために既に持っていると仮定します。)
ジェネレーターがありますarbitrary :: Gen (q,r,s,t)(ここで、q、r、s、t はすべて Arbitrary のインスタンスです)。しかし、それはarbitrary :: Gen a. つまり、aを表し(q,r,s,t)ます。uncurry4タイプ を持つ関数 があり(q -> r -> s -> t -> b) -> (q,r,s,t) -> bます。uncurry4 をSimpleTypeコンストラクターに適用することは明らかです。typeuncurry4 SimpleTypeも同様(q,r,s,t) -> SimpleTypeです。ただし、Hoogle は SimpleType を認識していないため、戻り値は一般的なものにしておきましょう。の定義を思い出すとa、本質的に が得られuncurry4 SimpleType :: a -> bます。
だから私は aGen aと functionを持っていますa -> b。そして結果が欲しいGen b。a(私たちの状況では、 is(q,r,s,t)とbisであることを思い出してくださいSimpleType)。だから私はこの型シグネチャを持つ関数を探しています: Gen a -> (a -> b) -> Gen b. それをフーグリングしGen、それが のインスタンスであることを知って、私は自分の問題に対するモナド的で魔法のような解決策であるMonadとすぐに認識します。liftM
Hoogle は再び危機を脱します。望ましい結果を得るには、おそらく何らかの「リフティング」コンビネータがあることは知っていましたが、正直なところ、型シグネチャを調べるまでは、liftM (durrr!) を使用することは考えていませんでした。