将 Either Reply [Maybe ByteString] 处理成 [Maybe Text]

Process a Either Reply [Maybe ByteString] into a [Maybe Text]

以下代码使用 Hedis 运行 mget 命令,return 结果为 [Maybe BS.ByteString]:

-- | Text to ByteString
tbs :: Text -> BS.ByteString
tbs = BS.pack . T.unpack


-- | ByteString to Text
bst :: BS.ByteString -> Text
bst = T.pack . BS.unpack


mgetRedis :: Redis.Connection -> [Text] -> IO [Maybe BS.ByteString]
mgetRedis connection keys =
    runRedis connection action
    where
        action = do

            result <- Redis.mget $ tbs <$> keys
            -- `result` is of type `Either Reply [Maybe BS.ByteString]`

            case result of
                Right values -> pure values
                _            -> pure []

首先,我觉得这段代码很乱,想知道是否有任何方法可以让它更干净。

其次,我想 mgetRedis 到 return 一个 [Maybe Text],使用上面写的 bst 助手。我无法执行 pure $ bst <$> values,因为这里有两个级别的解包:首先是 Maybe,然后是 List。有没有什么方法可以让这个函数 return 得到想要的类型,而不会淹没在嵌套的 case 语句的海洋中?

你问的本质上是如何 fmap 任意深入到一堆仿函数中。这可以通过编写 fmaps:

轻松完成
mgetRedis :: Redis.Connection -> [Text] -> IO [Maybe Text]
mgetRedis connection keys =
    runRedis connection action
    where
        action = do

            result <- Redis.mget $ tbs <$> keys
            -- `result` is of type `Either Reply [Maybe BS.ByteString]`

            case result of
                Right values -> pure $ fmap T.decodeUtf8 <$> values
                      -- equivalent to (fmap . fmap) T.decodeUtf8 $ values
                _            -> pure []

fmap . fmap 模式重复,即您可以使用 fmap . fmap . fmap 深度 fmap 三层。但在这一点上,将整个堆栈视为单个函子可能会更好,这可以通过 monad transformers.

来完成