Haskell beginner здесь, пытаясь обернуть API HTTP REST безопасным способом и с автоматическим декодированием Aeson возвращаемых значений. Я начал с функции Haskell для каждого вызова API. Это было немного условно, но хорошо.Моделирование POST API безопасным способом
Чтобы улучшить работу, я хотел бы превратить каждый вызов API в собственный тип данных. Например, для входа в систему я бы моделировал это как Login
тип, который задает метод, тип Credentials
для параметров метода и LoginReponse
для результата вызова API. Параметры и типы ответов, конечно, имеют соответствующие экземпляры FromJSON и ToJSON.
Для два API называет это выглядит примерно так (с использованием GADTs):
data Void = Void
data Credentials = Credentials {...}
data LoginResponse = LoginResponse {...}
data LogoutResponse = LogoutResponse {...}
data Command a where
Login :: Credentials -> Command LoginResponse
Logout :: Void -> Command LogoutResponse
execute :: FromJSON a => Command a -> IO a
execute cmd = do
manager <- newManager tlsManagerSettings
let request = buildHttpRequest cmd
result <- httpLbs request manager
let body = responseBody result
let parsed = fromJust $ decode body
return parsed
Это прекрасно работает для моего случая использования - я могу вникать команды перед их выполнением, я не могу построить недопустимые вызовов API и Aeson знает, как декодировать возвращаемые значения!
Только проблема с этим подходом заключается в том, что я должен хранить все мои Команды в одном файле под одной декларацией данных.
Я хотел бы переместить определение методов (в моем примере Login
и Logout
) для отдельных модулей, но, чтобы сохранить функцию execute
подобными, и, конечно, сохранить безопасность типа и декодирование эсона.
Я пытался что-то сделать с помощью классов типов, но не получил нигде.
Любые советы, как это сделать, приветствуются!
Я думаю, вас может заинтересовать [слуга] (https://hackage.haskell.org/package/servant). – Zeta
Во-первых, использование существующей библиотеки, такой как слуга, определенно является для вас способом для практических целей. Если речь идет об обучении, вы можете решить эту проблему, используя [семейства данных] (https://wiki.haskell.org/GHC/Type_families#Detailed_definition_of_data_families), что позволит вам по существу иметь «открытый» тип данных. Тогда вы можете иметь класс с функцией запроса 'buildHttpRequest', семейством данных' Command' и всем, что вам нужно.В качестве побочного примечания тип, который вы назвали «Void», обычно называется «()» в Haskell (Void обычно зарезервирован для типа * empty *). – user2407038