crypto-api包中有一个Crypto.Random API,用于指定“伪随机数生成器”的含义。
我使用System.Random的RandomGen类实例实现了这个API,即StdGen:
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中的所有内容都是CryptoRandomGen的成员?我希望能够在我自己的代码中执行此操作,而无需更改这两个库中的任何一个的源代码。我的直觉是将第一行改为
instance (RandomGen a) => CryptoRandomGen a where
但这似乎在语法上不正确。
答案 0 :(得分:5)
此处为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
所以你看,你可以分割生成器(或管理许多中间生成器),正确数量Int
s(或者在我的情况下,Word32
s),编码它们,然后返回字节。
因为RandomGen
仅限于生成(和拆分),所以没有任何直接的方法来支持实例化,重新实例化或查询种子长度等属性。
答案 1 :(得分:1)
据我所知,这是不可能的,除非你愿意打开UndecidableInstances
(当然,这可以让类型检查器进入无限循环)。这是一个示例,它使Monad
的每个实例都成为Functor
的实例:
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module Main
where
import Control.Monad (liftM)
instance (Monad a) => Functor a where
fmap = liftM
-- Test code
data MyState a = MyState { unM :: a }
deriving Show
instance Monad MyState where
return a = MyState a
(>>=) m k = k (unM m)
main :: IO ()
main = print . fmap (+ 1) . MyState $ 1
测试:
*Main> :main
MyState { unM = 2 }
在您的情况下,这转换为:
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
instance (RandomGen a) => CryptoRandomGen a where
newGen = ...
genSeedLength = ...
genBytes = ...
reseed = ...
顺便说一下,我曾经问过如何在没有UndecidableInstances
的情况下在haskell-cafe上实现这一点并获得this answer(与托马斯提出的相同的解决方法;我认为这很丑陋)。