haskell - 将 IO (Maybe (IO (Maybe a))) 减少到 IO (Maybe a)

标签 haskell monads function-signature

我有一个使用 HsOpenSsl 的 readPrivateKey 函数读取 Rsa key 的函数,不幸的是,我的函数的签名是这个 String -> IO (Maybe (IO Maybe RsaKey))。我需要 PEM 格式和 Cryptonite.RSA key ,我编写了函数 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 如果没有它,有什么方法可以做到这一点吗?

最佳答案

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

进行必要的包装/展开以使用 joinMaybeT m (MaybeT m a) -> MaybeT m a.

关于haskell - 将 IO (Maybe (IO (Maybe a))) 减少到 IO (Maybe a),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70445844/

相关文章:

unit-testing - Reactive Banana 1.0.0 - MomentIO() Monad 中的单元测试

haskell - 递归更新 Sodium 中的 "Behaviour"产生 'thread blocked ...'

scala - 在 Scala 中使用 monad 读取器的程序架构

c++ - 如何声明两个将彼此的签名作为参数的函数?

perl - 如何在签名中声明可选参数?

haskell - "reader"单子(monad)

haskell - 在 Haskell 中,如何区分一元函数定义中的可变引用与常规变量

haskell - 每个替代的 Monad 都是可过滤的吗?

haskell - 是否有一个 monad 没有相应的 monad 转换器(IO 除外)?

c++ - 如何在 *.inl 文件的模板类中定义模板函数