2011-12-11 13 views
1

crypto.Random APIがcrypto-apiパッケージ内にあり、何かが "擬似乱数ジェネレータ"であることを指定します。元のクラス宣言を変更せずに、型クラスのインスタンスが別の型クラスにあることを宣言します。

私はつまり、StdGen、System.RandomのRandomGenクラスのインスタンスを使用して、この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タイプのためですが、それは本当にシステムに何のために働くだろう.RandomのRandomGenのtypeclassです。

RandomGenのすべてが指定されたシム関数を使ってCryptoRandomGenのメンバーだと言う方法はありますか?これらの2つのライブラリのいずれかのソースを変更することなく、私自身のコードでこれを行うことができます。私の本能は、最初の行を次のように変更することです:

instance (RandomGen a) => CryptoRandomGen a where 

しかし、それは構文的に正しいとは思われません。

答えて

5

ここではCrypto-APIの著者です。これをしないでください。本当にCryptoRandomGenの暗黙のプロパティに違反しています。

私はそれをどうしたらいいですか?あなたのRandomGenをラップするnewtypeを作成し、その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 

だから、あなたは発電機を分割する(または多くの中間の発電機を管理する)、Int S(または私の場合は、Word32 S)の右の数を作り、それらをコードし、バイトを返すことができ、参照してください。

RandomGenは生成(分割)のみに限定されているため、整理、再インスタンス化、またはシード長などのプロパティのクエリをサポートするための簡単な方法はありません。

+0

ありがとうございます。 crypto-apiが必要とするセマンティクスをサポートしていないSystem.Randomについて、あなたが何を言っているかを完全に見ています。私は、おそらく楽しみのためのエクササイズとして、他の方法(RandomGenクラスのすべてのCryptoRandomGenインスタンスを作成する)を実装しようとします。しかし、私があなたのコードを読んでいるならば、呼び出し元は型チェックをするためにこの醜いACRG関数でRandomGenをラップアップしなければなりません。私はUndecidableInstancesを有効にするのが醜いことに対してそれを考慮する必要があるように見えます。 – Litherum

+0

はい、あなたは正しいことを読んでいます。それは、クラスの特定のインスタンスを選択するために新しいタイプを作る、一般的なトリックです。 –

1

私が知る限り、UndecidableInstances(もちろん、型チェッカーを無限ループにすることができます)を有効にしない限り、これは不可能です。ここMonadFunctorのインスタンスのすべてのインスタンスを作る例です:

{-# 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 = ... 

、私は一度haskell-cafeでUndecidableInstancesを使わずにこれを実装する方法を尋ねられ、this answerを得ました(Thomasが提案したのと同じ回避策ですそれは醜いです)。

+0

この場合、タイプチェッカーは無限ループになることはまずありません。しかし、Monadの全てのインスタンスに対してFunctorの "OverreachingInstance"があるので、(それ自身のインスタンスを持っている)与えられたMonadに対してどのインスタンスを使うかの選択は、コンパイル順に依存します。 –

関連する問題