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

2024-01-25

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

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

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

instance (RandomGen a) => CryptoRandomGen a where

但这在语法上似乎不正确。


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

也就是说,我会这样做:只需制作一个新类型来包装您的RandomGen并使该新类型成为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

所以你看,你可以分割生成器(或管理许多中间生成器),制作正确数量的Ints(或者就我而言,Word32s),对它们进行编码,并返回字节。

Because RandomGen仅限于生成(和分割),没有任何直接的方法来支持实例化、重新实例化或查询种子长度等属性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐