将 IO (Maybe (IO (Maybe a)) 减少为 IO (Maybe a)
Reduce IO (Maybe (IO (Maybe a)) to IO (Maybe a)
我有一个函数可以使用 HsOpenSsl 的 readPrivateKey
函数读取 Rsa 密钥,不幸的是我函数的签名是这个 String -> IO (Maybe (IO Maybe RsaKey))
。我需要 PEM 格式和一个 Cryptonite.RSA 密钥,我编写了函数 mkRsaKey
以从 PEM 格式的字符串中生成它。
代码如下:
import qualified Crypto.PubKey.RSA as Rsa --from cryptonite
import OpenSSL.EVP.PKey -- from HsOpenSSL
import OpenSSL.PEM -- from HsOpenSSL
import OpenSSL.RSA -- from HsOpenSSL
import Prelude
data RsaKey = RsaKey
{ rsaKeyCryptoniteKey :: Rsa.PrivateKey,
rsaKeyStringRepr :: String
}
deriving (Show)
openSslKeyToCryptoniteKey :: RSAKeyPair -> Maybe Rsa.PrivateKey
openSslKeyToCryptoniteKey key = do
let d = rsaD key
let p = rsaP key
let q = rsaQ key
let mdP = rsaDMP1 key
let mdQ = rsaDMQ1 key
let mqinv = rsaIQMP key
let size = rsaSize key
let n = rsaN key
let e = rsaE key
dP <- mdP
dQ <- mdQ
qinv <- mqinv
let pub = Rsa.PublicKey size n e
return $ Rsa.PrivateKey pub d p q dP dQ qinv
openSslKeyToRsaKey :: RSAKeyPair -> IO (Maybe RsaKey)
openSslKeyToRsaKey key = do
stringRepr <- writePublicKey key
let maybeCryptoKey = openSslKeyToCryptoniteKey key
return $ do
cryptoKey <- maybeCryptoKey
return $ RsaKey cryptoKey stringRepr
mkRsaKey :: String -> IO (Maybe (IO (Maybe RsaKey)))
mkRsaKey privateKey = do
someOpenSslKey <- readPrivateKey privateKey PwNone
let openSslKey = toKeyPair someOpenSslKey
return $ openSslKeyToRsaKey <$> openSslKey
现在你可以看到类型签名在我看来不是最优的我想要 IO (Maybe RsaKey)
。我怎样才能做到这一点?
编辑:
我实际上做到了,但我正在使用 unsafePerformIO
:
mkRsaKey :: String -> IO (Maybe RsaKey)
mkRsaKey privateKey = do
someOpenSslKey <- readPrivateKey privateKey PwNone
return $ do
openSslKey <- toKeyPair someOpenSslKey
unsafePerformIO (openSslKeyToRsaKey $ openSslKey)
据我所知,你永远不应该使用 unsafePerformIO
如果没有它,有什么方法可以做到这一点吗?
找到了一种无需 unsafePerformIO
的方法,诀窍是使用 case 语句,该语句仅在 Nothing
情况下使用 return 函数。这是实现:
mkRsaKey :: String -> IO (Maybe RsaKey)
mkRsaKey privateKey = do
someOpenSslKey <- readPrivateKey privateKey PwNone
let maybeOpenSslKey = toKeyPair someOpenSslKey
case maybeOpenSslKey of
Just key -> openSslKeyToRsaKey key
Nothing -> return Nothing
case
发现不错。这绝对不是您应该使用 unsafePerformIO
的地方。这是一个更紧凑的方式,为了好玩。
flattenMaybe :: (Monad m) => m (Maybe (m (Maybe a))) -> m (Maybe a)
flattenMaybe m = m >>= fromMaybe (return Nothing)
为了额外的乐趣,像这样展平层的能力是 monad 的一个特征;我们只是在 m (Maybe ...)
上使用该功能,也称为 MaybeT
。所以我们也可以这样写:
flattenMaybe = runMaybeT . join . fmap MaybeT . MaybeT
做必要的 wrapping/unwrapping 在 MaybeT m (MaybeT m a) -> MaybeT m a
使用 join
。
我有一个函数可以使用 HsOpenSsl 的 readPrivateKey
函数读取 Rsa 密钥,不幸的是我函数的签名是这个 String -> IO (Maybe (IO Maybe RsaKey))
。我需要 PEM 格式和一个 Cryptonite.RSA 密钥,我编写了函数 mkRsaKey
以从 PEM 格式的字符串中生成它。
代码如下:
import qualified Crypto.PubKey.RSA as Rsa --from cryptonite
import OpenSSL.EVP.PKey -- from HsOpenSSL
import OpenSSL.PEM -- from HsOpenSSL
import OpenSSL.RSA -- from HsOpenSSL
import Prelude
data RsaKey = RsaKey
{ rsaKeyCryptoniteKey :: Rsa.PrivateKey,
rsaKeyStringRepr :: String
}
deriving (Show)
openSslKeyToCryptoniteKey :: RSAKeyPair -> Maybe Rsa.PrivateKey
openSslKeyToCryptoniteKey key = do
let d = rsaD key
let p = rsaP key
let q = rsaQ key
let mdP = rsaDMP1 key
let mdQ = rsaDMQ1 key
let mqinv = rsaIQMP key
let size = rsaSize key
let n = rsaN key
let e = rsaE key
dP <- mdP
dQ <- mdQ
qinv <- mqinv
let pub = Rsa.PublicKey size n e
return $ Rsa.PrivateKey pub d p q dP dQ qinv
openSslKeyToRsaKey :: RSAKeyPair -> IO (Maybe RsaKey)
openSslKeyToRsaKey key = do
stringRepr <- writePublicKey key
let maybeCryptoKey = openSslKeyToCryptoniteKey key
return $ do
cryptoKey <- maybeCryptoKey
return $ RsaKey cryptoKey stringRepr
mkRsaKey :: String -> IO (Maybe (IO (Maybe RsaKey)))
mkRsaKey privateKey = do
someOpenSslKey <- readPrivateKey privateKey PwNone
let openSslKey = toKeyPair someOpenSslKey
return $ openSslKeyToRsaKey <$> openSslKey
现在你可以看到类型签名在我看来不是最优的我想要 IO (Maybe RsaKey)
。我怎样才能做到这一点?
编辑:
我实际上做到了,但我正在使用 unsafePerformIO
:
mkRsaKey :: String -> IO (Maybe RsaKey)
mkRsaKey privateKey = do
someOpenSslKey <- readPrivateKey privateKey PwNone
return $ do
openSslKey <- toKeyPair someOpenSslKey
unsafePerformIO (openSslKeyToRsaKey $ openSslKey)
据我所知,你永远不应该使用 unsafePerformIO
如果没有它,有什么方法可以做到这一点吗?
找到了一种无需 unsafePerformIO
的方法,诀窍是使用 case 语句,该语句仅在 Nothing
情况下使用 return 函数。这是实现:
mkRsaKey :: String -> IO (Maybe RsaKey)
mkRsaKey privateKey = do
someOpenSslKey <- readPrivateKey privateKey PwNone
let maybeOpenSslKey = toKeyPair someOpenSslKey
case maybeOpenSslKey of
Just key -> openSslKeyToRsaKey key
Nothing -> return Nothing
case
发现不错。这绝对不是您应该使用 unsafePerformIO
的地方。这是一个更紧凑的方式,为了好玩。
flattenMaybe :: (Monad m) => m (Maybe (m (Maybe a))) -> m (Maybe a)
flattenMaybe m = m >>= fromMaybe (return Nothing)
为了额外的乐趣,像这样展平层的能力是 monad 的一个特征;我们只是在 m (Maybe ...)
上使用该功能,也称为 MaybeT
。所以我们也可以这样写:
flattenMaybe = runMaybeT . join . fmap MaybeT . MaybeT
做必要的 wrapping/unwrapping 在 MaybeT m (MaybeT m a) -> MaybeT m a
使用 join
。