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) =<<)
Понятно, что это накладывает дополнительное ограничение на тип ошибки с левой стороны, поэтому я не уверен, что это приемлемо. Если нет, пожалуйста, оставьте комментарий.