Haskell / Wreq - 关于 http 请求的复杂类型签名的建议

Haskell / Wreq - Advice on complicated type signatures for http requests

我是一个 Haskell 初学者,目前正在使用 wreq 围绕 api 制作一个简单的包装器。如果有时间,我想发送 if-modified-since header。我按以下方式这样做。

getResponse :: (FormatTime t, Exception e) => File -> Maybe t -> IO (Either e (Response L.ByteString))
getResponse file (Just t) =
  let formattedTime = (B.pack . formatTime defaultTimeLocale rfc822DateFormat) t
      opts = defaults & header "if-modified-since" .~ [formattedTime]
  in try $ getWith opts $ buildUrl file

getResponse file Nothing = try $ (get $ buildUrl file)

我注意到 304 (not modified) 响应作为异常返回,因此这是我使用 Either 类型的理由。我想为可能使用此 api 包装器的人提供错误可见性。

假设请求成功,我想将响应 body 解析为我的库中定义的相应类型。如果我向其发出请求的服务器发生某些变化,反序列化可能无法正常工作,因此我选择使用 Maybe 类型来解决这个问题。

getPayload :: FromJSON b => (Either e (Response L.ByteString)) -> Either e (Maybe b)
getPayload (Left _)  = return Nothing
getPayload (Right a) = return $ fmap Just (^. responseBody) =<< asJSON a

这些函数的签名开始让我觉得碍眼,我的直觉告诉我有更好的方法,但我不确定。我做的一件事是制作另一个功能将它们放在一起,希望它更容易使用。这是我计划用来创建其他函数以对单个资源提出更具体请求的函数。

getResource :: (Exception e, FormatTime t, FromJSON b) => File -> Maybe t -> IO (Either e (Maybe b))
getResource f t =  getPayload <$> (getResponse f t)

我现在在处理 http 请求时必须处理 3 层结构。 IOEitherMaybe。我是不是太复杂了?从使用和可维护性的角度来看,我该怎么做才能减轻这种工作的痛苦?我该如何改进?

这可能不是您想要的,但是 asJSON 具有 return 类型 m (Response a),其中 mMonadThrow。虽然 Maybe 是一个 MonadThrow 实例,但 Either e 也是。这意味着如果 asJSON 出现任何问题,您 不必 使用 Maybe 来处理。您可以 'stay' 在 Either monad 中代替:

getPayload :: FromJSON b => Either SomeException (Response L.ByteString)
                         -> Either SomeException b
getPayload = ((fmap (^. responseBody) . asJSON) =<<)

显然,这对左侧的错误类型施加了额外的限制,因此我不确定这是否可以接受。如果没有,请发表评论。