如何将 IO ByteString 转换为 IO String

How can I convert IO ByteString to IO String

我知道如何通过解包将 ByteString 转换为 String,但我正在努力弄清楚如何将 IO ByteString(这是我从 HaskellNet 中的 fetchHeader 函数返回的)转换为 IO 字符串。我基本上是想做这样的事情

getAllHeadersForMessageUID :: IMapConnection -> UID -> IO String
getAllHeadersForMessageUID connection uid = do
   headers <- fetchHeader connection uid  
   return (headers  >>= BS.unpack)

错误消息对我来说没有意义

Couldn't match expected type ‘[BS.ByteString]’
            with actual type ‘BS.ByteString’
In the first argument of ‘(>>=)’, namely ‘headers’
In the first argument of ‘return’, namely ‘(headers >>= BS.unpack)

我不知道为什么需要一个 ByteString 列表。

尝试使用 return $ BS.unpack headers 而不是 return (headers >>= BS.unpack)

或者尝试 return $ map BS.unpack headers 如果 headers 是一个 ByteString 列表。

除了它碰巧会进行类型检查(我假设 BS.unpack headers 有效)之外,还有一种思考方式:

  • headers是纯值
  • BS.unpack 是纯函数
  • headers >>= ... 没有意义,因为 >>= 的 LHS 需要是一元计算
  • ... >>= BS.unpack 没有意义,因为 >>= 的 RHS 需要是一个产生单子计算的函数
  • BS.unpack headers是我们要的字符串return,但是是一个纯值
  • 因此我们使用 return 将纯值提升为一元计算

更新:

以下代码表明,如果 fetchHeader 的类型为 IO [BS.ByteString],那么您的代码将进行类型检查:

import Data.ByteString.Char8 as BS

fetchHeader :: IO [BS.ByteString]   -- this works
-- fetchHeader :: IO BS.ByteString  -- this doesn't
fetchHeader = undefined

foo :: IO String
foo = do
  headers <- fetchHeader
  return $ headers >>= BS.unpack

另一方面,如果您将其类型更改为 IO BS.ByteString,则会出现您遇到的错误。

更新 2:

有趣的是,当 headers 是 ByteString 列表时,表达式 headers >>= BS.unpack 确实有意义并且等效于:

concat $ map BS.unpack headers

User5402 的回答假定 ByteString 是纯 ASCII(如果您是唯一使用您的代码的人,这没问题,但有几个原因表明如果您打算共享它是个坏主意) 如果 ByteString 使用 UTF-8 编码:您可以像这样将其转换为 String:

import qualified Codec.Binary.UTF8.String as UTF8
foo b = do
  bs <- b
  return $ UTF8.decode $ unpack bs

我不确定如何处理其他编码,例如 windows 代码页(除了设置句柄编码,此处不适用)。