声明类型类的所有实例都在另一个类型类中,而不修改原始类声明

Lit*_*rum 1 haskell subclass instance typeclass

crypto-api包中有一个Crypto.Random API,用于指定"伪随机数生成器"的含义.

我使用System.Random的RandomGen类的实例,即StdGen实现了这个API:

instance CryptoRandomGen StdGen where
  newGen bs = Right $ mkStdGen $ shift e1 24 + shift e2 16 + shift e3 8 + e4
    where (e1 : e2 : e3 : e4 : _) = Prelude.map fromIntegral $ unpack bs
  genSeedLength = Tagged 4
  genBytes n g = Right $ genBytesHelper n empty g
    where genBytesHelper 0 partial gen = (partial, gen)
          genBytesHelper n partial gen = genBytesHelper (n-1) (partial `snoc` nextitem) newgen
            where (nextitem, newgen) = randomR (0, 255) gen
  reseed bs _ = newGen bs
Run Code Online (Sandbox Code Playgroud)

但是,此实现仅适用于StdGen类型,但它确实适用于System.Random的RandomGen类型类中的任何内容.

有没有办法说使用给定的填充函数,RandomGen中的所有内容都是CryptoRandomGen的成员?我希望能够在我自己的代码中执行此操作,而无需更改这两个库中的任何一个的源代码.我的直觉是将第一行改为类似的东西

instance (RandomGen a) => CryptoRandomGen a where
Run Code Online (Sandbox Code Playgroud)

但这似乎在语法上并不正确.

Tho*_*son 5

Crypto-API的作者在这里.请不要这样做 - 这实际上违反了CryptoRandomGen的隐式属性.

也就是说,我就是这样做的:只需创建一个包装你的新类型RandomGen,并将该newtype作为一个实例CryptoRandomGen.

newtype AsCRG g = ACRG { unACRG :: g}

instance RandomGen g => CryptoRandomGen (AsCRG g) where
    newGen = -- This is not possible to implement with only a 'RandomGen' constraint.  Perhaps you want a 'Default' instance too?
    genSeedLength = -- This is also not possible from just 'RandomGen'
    genBytes nr g =
        let (g1,g2) = split g
            randInts :: [Word32]
            randInts = B.concat . map Data.Serialize.encode
                     . take ((nr + 3) `div` 4)
                     $ (randoms g1 :: [Word32])
        in (B.take nr randInts, g2)
    reseed _ _ = -- not possible w/o more constraints
    newGenIO = -- not possible w/o more constraints
Run Code Online (Sandbox Code Playgroud)

所以你看,你可以分割生成器(或管理许多中间生成器),生成正确数量的Ints(或者在我的情况下,Word32s),编码它们,并返回字节.

因为RandomGen仅限于生成(和拆分),所以没有任何直接的方法来支持实例化,重新实例化或查询诸如种子长度之类的属性.