对于大量代数数据类型,一般派生 Arbitrary?

Generically derive Arbitrary for massive algebraic data types?

我有一个协议,我是这样输入的:

data ProtocolPacket
  = Packet1 Word8 Text Int8
  | Packet2 Text
  | Packet3 Int Text Text Text Text
  | Packet4 Int Double Double Double Int16 Int16 Int16
  ...
  deriving (Show,Eq)

此外,我还为每个数据包实现了 serialization/deserialization 代码。当然,我想在 Quickcheck 中测试这个协议,并确保序列化和反序列化任何输入组合的任何数据包都会准确地返回我输入的内容。所以我继续为 Arbitrary 实现这些数据包像这样输入 class:

instance Arbitrary ProtocolPacket where
  arbitrary = do
  packetID <- choose (0x00,...) :: Gen Word8
  case packetID of
    0x00 -> do
      a <- arbitrary
      b <- arbitrary
      c <- arbitrary
      return $ Packet1 a b c
    0x01 -> do
      a <- arbitrary
      return $ Packet2 a
    0x02 -> do
      a <- arbitrary
      b <- arbitrary
      c <- arbitrary
      d <- arbitrary
      e <- arbitrary
      return $ Packet3 a b c d e
    0x03 -> do
      a <- arbitrary
      b <- arbitrary
      c <- arbitrary
      d <- arbitrary
      e <- arbitrary
      f <- arbitrary
      g <- arbitrary
      return $ Packet4 a b c d e f g
    ...

假设我已经为所有没有立即定义 Arbitrary 的相关数据构造函数参数定义了 Arbitrary,这样的代码需要由我为了让数据包字段填充有意义的数据。但仅此而已。

但是如您所见,我正在重复自己 很多 只是为了一些繁重的工作。这是我实际处理的一小部分样本。理想情况下,我希望能够做到这一点:

{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics

data ProtocolPacket
  = Packet1 Word8 Text Int8
  | Packet2 Text
  | Packet3 Int Text Text Text Text
  | Packet4 Int Double Double Double Int16 Int16 Int16
  ...
  deriving (Show,Eq,Generic)

instance Arbitrary ProtocolPacket

就像我可以用 FromJSONToJSON 做的那样,但这不起作用。有什么方法可以吗?

Daniel Wagner 在评论中提到 generic-random is capable of doing this. It was the library I was looking for, but the docs didn't make it obvious to me that such was the case. Recent to the time of this writing, Brent Yorgey posted a pretty clear tutorial on his blog that went into detail as to how to use generic-random to do what I was asking about and more. The blog post can be found here.

对于我的情况,解决方案很简单。使用 Generic.Random.Generic 来自 generic-random:

{-# LANGUAGE DeriveGeneric #-}
import Generic.Random.Generic
import GHC.Generics

data ProtocolPacket
  = Packet1 Word8 Text Int8
  | Packet2 Text
  | Packet3 Int Text Text Text Text
  | Packet4 Int Double Double Double Int16 Int16 Int16
  ...
  deriving (Show,Eq,Generic)

instance Arbitrary ProtocolPacket where
  arbitrary = genericArbitrary