Как я должен создать структуру данных из нескольких сетевых запросов в Haskell
Я новичок в Haskell, поэтому заранее извиняюсь за потенциально глупый вопрос.
Я хотел бы построить структуру данных, которая состоит из двух запросов http в моем приложении.
Мой первый запрос получает базовый список пользователей, которых я могу выбрать decode
в Maybe [User]
r <- getWith opts "https://www.example.com/users"
let users = decode $ r ^. responseBody :: Maybe [User]
Но если бы я хотел обогатить свои пользовательские данные, вызвав вторую конечную точку для каждого из пользователей, которые отвечают, выполняя что-то вроде
r2 <- getWth opts "https://www.example.com/users/{userid}/addresses"
let enrichedUser = decode $ r2 ^. responseBody :: Maybe EnrichedUser
Я не могу собрать эти части в одну минуту. Я в do
блок, который ожидает IO ()
Любая помощь будет оценена!
1 ответ
Я предполагаю, что тип enrichedUser
должен быть Maybe EnrichedUser
и не Maybe [EnrichedUser]
, право?
Если это так, после извлечения [User]
список из users :: Maybe [User]
проблема, с которой вы сталкиваетесь, заключается в выполнении монадического действия (для извлечения веб-страницы) для каждого User
, Для этого есть удобный комбинатор Control.Monad
:
mapM :: (Monad m) => (a -> m b) -> ([a] -> m [b])
который может быть специализирован в вашей ситуации, чтобы:
mapM :: (User -> IO EnrichedUser) -> ([User] -> IO [EnrichedUser])
Это говорит, если вы знаете, как написать функцию, которая принимает User
и создает действие ввода-вывода, которое создаст EnrichedUser
, ты можешь использовать mapM
чтобы превратить это в функцию, которая принимает список [User]
и создает действие ввода-вывода для создания целого списка [EnrichedUser]
,
Я полагаю, что в вашем приложении бывшая функция будет выглядеть примерно так:
enrich :: User -> IO EnrichedUser
enrich u = do
let opts = ...
let url = "https://www.example.com/users/"
++ userToUserID u ++ "/addresses"
r2 <- getWith opts url
let Just enrichedUser = decode $ r2 ^. responseBody
return enrichedUser
where decode = ...
и тогда вы можете написать (в вашем блоке ввода / вывода):
r <- getWith opts "https://www.example.com/users"
let Just users = decode $ r ^. responseBody
enrichedUsers <- mapM enrich users
-- here, enrichedUsers :: [EnrichedUser]
...etc...
Я опустил Maybe
обработка здесь для простоты. Если обогащение не удается, вы, вероятно, хотите каким-то образом User
в дефолт EnrichedUser
в любом случае, так что вы измените нижнюю часть enrich
функция для чтения:
let enrichedUser = case decode $ r2 ^. responseBody of
Nothing -> defaultEnrichment u
Just e -> e
return enrichedUser
и все остальное останется таким же.