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

Nic*_*sta 6 haskell types functional-programming wreq

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

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)
Run Code Online (Sandbox Code Playgroud)

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

假设请求成功,我想将响应主体解析为我库中定义的相应类型.如果服务器上的某些内容发生了变化,我可能无法正常反序列化,因此我选择使用该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
Run Code Online (Sandbox Code Playgroud)

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

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

在处理http请求时,我现在必须处理3层结构. IO,EitherMaybe.我这太复杂了吗?从使用和可维护性的角度来看,我能做些什么来减少这种痛苦?我怎样才能改善这个?

Mar*_*ann 2

这可能不是您想要的,但asJSON返回类型为m (Response a),其中mMonadThrow。WhileMaybe是一个MonadThrow实例,那么 也是Either e。这意味着您不必使用Maybe处理asJSON. 你可以“留在” Eithermonad 中:

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

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