Haskell HTTP 响应中 ByteString 的笨拙类型签名

Clumsy Looking Type Signature for ByteStrings in Haskell HTTP Response

我正在试验 http-conduit 库并有这个简单的例子:

#!/usr/bin/env stack
{- stack
    --resolver lts-7.12
    --install-ghc
    runghc
    --package http-conduit
-}
{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Conduit
import Data.ByteString.Lazy.Internal

getUrl :: IO (Data.ByteString.Lazy.Internal.ByteString) ---eeew!
getUrl = do
  resp <- simpleHttp "http://www.whosebug.com"
  return resp

我从 了解到,我应该更喜欢将响应作为 ByteString 而不是 [Char] 或 String。我假设 OverloadedStrings pragma 可能会减少这个问题,但对于我的输出类型似乎没有改变。

该函数工作正常,尽职尽责地为 SO 的主页打印出一个简单的 http 响应,但该类型签名看起来真的很难看:

getUrl :: IO (Data.ByteString.Lazy.Internal.ByteString)

而且我不得不说我从 Internet 示例中很少看到类似的东西(我们有比椭圆形 Java 导入更多的点,伙计)。那正确吗?如果我想 return 一个响应然后开始解析它,比如使用 HXT 或 tagsoup 或 attoparsec,这是正确的方法还是类型签名?

我注意到,例如,当我开始添加接受参数的能力时,这变得更加丑陋,例如提供不同的 URL:

import Network.HTTP.Conduit
import Data.ByteString.Lazy.Internal

-- alright, first arg is now string for my url...
getUrl :: String -> IO (Data.ByteString.Lazy.Internal.ByteString)
getUrl url = do
  resp <- simpleHttp url
  return resp


main :: IO (ByteString)  -- what?!  inside the () ?
main = do
  getUrl "https://www.whosebug.com"

这似乎不健康。我应该如何理解如何正确构建它?

您始终可以使用限定路径编写类型。但是,由于您已经导入 Data.ByteString.Lazy.Internal 并且除非您在范围内有其他 ByteString 类型(如严格类型),您可以简单地省略 Data.ByteString.Lazy.Internal:

getUrl :: IO ByteString

此外,除非您有特殊需要导入 Internal,否则我建议您只导入 Data.ByteString.Lazy(它还会导出您正在使用的惰性 ByteString 类型)。如果范围内同时有严格和惰性 ByteString,我会导入它们:

 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as BL

那么 BS.ByteString 是严格类型,BL.ByteString 是惰性类型 - 不需要写出完整的 Data.ByteString.ByteStringData.ByteString.Lazy.ByteString.

还值得一提(感谢@duplode),通常最好导入任何一个 bytestring 模块,因为它们定义了一大堆与 Prelude(以及彼此)冲突的函数。

最后,请注意类型周围的括号本身没有任何作用。 (ByteString)ByteString 是相同的。因此,我不会包括它们。


与你的问题无关,但对你的单子代码有一些评论:

  • 每次你写这样的东西

    do x <- e
       return x
    

    可以直接替换为e.

  • 每次你写这样的东西

    do e
    

    可以换成e.