Как я должен создать структуру данных из нескольких сетевых запросов в 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

и все остальное останется таким же.

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