Haskell / Wreq - Советы по подписи сложных типов для http запросов

Я новичок в 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)

Я заметил, что 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

Подписи этих функций начинают казаться мне бельмом на глазу, что-то в моем кишечнике говорит мне, что есть лучший способ, но я не уверен. Одна вещь, которую я сделал, это сделала другую функцию, чтобы соединить их вместе с надеждами, что их будет проще использовать. Это функция, которую я планирую использовать для создания других функций, чтобы делать более конкретные запросы к отдельным ресурсам.

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

Теперь мне приходится иметь дело с 3 уровнями структуры при работе с http-запросом. IO, Either, а также Maybe, Я делаю это слишком сложным? Что я могу сделать, чтобы с этим было легче работать с точки зрения использования и удобства обслуживания? Как я могу улучшить это?

1 ответ

Решение

Это может быть не совсем то, что вы хотите, но asJSON имеет тип возврата m (Response a), где m это MonadThrow, В то время как Maybe это MonadThrow экземпляр, то так и есть Either e, Это означает, что вам не нужно использовать Maybe для того, чтобы справиться, если что-то пойдет не так с asJSON, Вы можете "остаться" в Either вместо монады:

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

Понятно, что это накладывает дополнительное ограничение на тип ошибки с левой стороны, поэтому я не уверен, что это приемлемо. Если нет, пожалуйста, оставьте комментарий.

Другие вопросы по тегам