Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 声明一个typeclass的所有实例都在另一个typeclass中,而不修改原始类声明_Haskell_Instance_Subclass_Typeclass - Fatal编程技术网

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

Haskell 声明一个typeclass的所有实例都在另一个typeclass中,而不修改原始类声明,haskell,instance,subclass,typeclass,Haskell,Instance,Subclass,Typeclass,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 : _) = Pr

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 typeclass中的任何内容

有没有一种方法可以说RandomGen中的所有内容都是使用给定垫片函数的CryptoRandomGen的成员?我希望能够在自己的代码中做到这一点,而不必更改这两个库中任何一个的源代码。我的直觉是把第一行改成

instance (RandomGen a) => CryptoRandomGen a where

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

据我所知,这是不可能的,除非您愿意打开(当然,这会使typechecker进入无限循环)。下面的示例使
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 = ...

作为一个旁白,我曾经问过如何在Haskell咖啡馆里实现“没有代码>未解密的实例< /代码>,并得到了(托马斯提出的同样的解决办法;我认为它很难看)。下面的示例使
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 = ...

作为一个旁白,我曾经问过如何在Haskell咖啡馆中实现“没有代码>未解密的实例< /代码>,并得到了(托马斯提出的同样的解决方案;我认为它很难看)。请不要这样做-这确实违反了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
仅限于生成(和拆分),因此没有任何直接的方法来支持安装、重新安装或查询属性,如种子长度。

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
仅限于生成(和拆分),因此没有任何直接的方法来支持安装、重新安装或查询属性(如种子长度)。

谢谢;我完全明白您对System.Random的看法,它不支持crypto api所要求的语义。我可能会尝试以另一种方式实现(使每个CryptoRandomGen都成为RandomGen类的实例),只是为了好玩。但是,如果我正确地阅读了您的代码,调用方仍然必须将其RandomGen封装在这个丑陋的ACRG函数中,才能检查类型。看来我得把它和打开不可判定的实例的丑陋性权衡一下。是的,你读得对。这是一个常见的技巧,创建一个新类型以选择一个类的特定实例;我完全明白您对System.Random的看法,它不支持crypto api所要求的语义。我可能会尝试以另一种方式实现(使每个CryptoRandomGen都成为RandomGen类的实例),只是为了好玩。但是,如果我正确地阅读了您的代码,调用方仍然必须将其RandomGen封装在这个丑陋的ACRG函数中,才能检查类型。看来我得把它和打开不可判定的实例的丑陋性权衡一下。是的,你读得对。这是一个常见的技巧,创建一个新类型以选择一个类的特定实例。在这种情况下,类型检查器不太可能进入无限循环。但是,由于所有Monad实例都有一个“过度反应实例”Functor,因此对给定Monad使用哪个实例(当它有自己的实例时)取决于编译顺序-也不好。在这种情况下,类型检查器不太可能进入无限循环。但是,由于所有Monad实例都有一个“过度反应实例”Functor,因此对给定Monad使用哪个实例(当它有自己的实例时)取决于编译顺序,这也不好。