提问人:Gian Laager 提问时间:12/22/2021 最后编辑:Gian Laager 更新时间:12/22/2021 访问量:207
将 IO(也许 (IO (也许 a)))减少到 IO (也许 a)
Reduce IO (Maybe (IO (Maybe a)) to IO (Maybe a)
问:
我有一个函数,它使用 HsOpenSsl 的函数读取 Rsa 密钥,不幸的是,我的函数的签名是这个。我需要 PEM 格式和 Cryptonite.RSA 密钥,我编写了函数来从 PEM 格式的字符串中生成它。readPrivateKey
String -> IO (Maybe (IO Maybe RsaKey))
mkRsaKey
代码如下:
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
答:
4赞
Gian Laager
12/22/2021
#1
找到一种没有技巧的方法是使用一个 case 语句,该语句仅在 case 中使用 return 函数。实现如下:unsafePerformIO
Nothing
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
评论
5赞
jlwoodwa
12/22/2021
如果您不知道,案例表达式的审查者本身可以是一个表达式;它不一定是一个简单的变量。
0赞
chepner
12/22/2021
也:。maybeOpenSslKey <- toKeyPair <$> readPrivateKey privateKey PwNone
1赞
Gian Laager
12/23/2021
@jlwoodwa我知道,但我发现这样更可读
5赞
luqui
12/22/2021
#2
不错的发现。这绝对不是您应该使用的地方。为了好玩,这里有一个更紧凑的方式。case
unsafePerformIO
flattenMaybe :: (Monad m) => m (Maybe (m (Maybe a))) -> m (Maybe a)
flattenMaybe m = m >>= fromMaybe (return Nothing)
为了获得额外的乐趣,像这样展平层的能力是单子的特征能力;我们只是在 上使用该能力,也称为 MaybeT
。所以我们也可以这样写:m (Maybe ...)
flattenMaybe = runMaybeT . join . fmap MaybeT . MaybeT
执行必要的换行/解包以在 处使用联接
。MaybeT m (MaybeT m a) -> MaybeT m a
评论
2赞
amalloy
12/22/2021
我认为值得注意的是,如果一开始就用 MaybeT 编写,那么您根本不需要。openSslKeyToRsaKey
mkRsaKey
flattenMaybe
1赞
dfeuer
12/23/2021
join . fmap MaybeT = (>>= MaybeT)
上一个:与 C++11 函数签名不一致
下一个:如何替代通用匿名函数?
评论